From 2f4eb39156fe25ff93e8092e351695719e474bd0 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 22 Nov 2016 08:45:38 +0100 Subject: [PATCH 1/2] Jippie! Finally got the Sub Statemachine declaration understandable for end-users. Reworked the hierarchical state machine processing in terms of sub processing. Now, the declaration and usage of subs is completely understandable and easy to implement. I am excited to see how end-users will see the possibilities. --- Moose Development/Moose/Core/Base.lua | 25 +++ Moose Development/Moose/Core/Event.lua | 9 +- Moose Development/Moose/Core/StateMachine.lua | 140 +++++++++----- Moose Development/Moose/Fsm/Process.lua | 14 +- Moose Development/Moose/Process/Account.lua | 21 ++- Moose Development/Moose/Process/Assign.lua | 8 + Moose Development/Moose/Process/Route.lua | 29 ++- Moose Development/Moose/Process/Smoke.lua | 13 +- Moose Development/Moose/Tasking/Task.lua | 175 ++++++++---------- Moose Development/Moose/Tasking/Task_SEAD.lua | 5 +- .../Moose_Test_Task_SEAD.lua | 36 ++-- .../Moose_Test_Task_SEAD.miz | Bin 26262 -> 25960 bytes .../DCS World - MOOSE - Tasking - SEAD.pptx | Bin 2757198 -> 2757094 bytes 13 files changed, 296 insertions(+), 179 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 9db404093..9ceb850bb 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -116,9 +116,18 @@ function BASE:New() self.__index = self _ClassID = _ClassID + 1 self.ClassID = _ClassID + + return self end +function BASE:_Destructor() + --self:E("_Destructor") + + self:EventRemoveAll() +end + + --- This is the worker method to inherit from a parent class. -- @param #BASE self -- @param Child is the Child class that inherits. @@ -131,6 +140,22 @@ function BASE:Inherit( Child, Parent ) if Child ~= nil then setmetatable( Child, Parent ) Child.__index = Child + + local proxy = newproxy(true) + local proxyMeta = getmetatable(proxy) + + proxyMeta.__gc = function () + -- env.info("In __gc for " .. Child:GetClassNameAndID() ) + if Child._Destructor then + Child:_Destructor() + end + end + + -- keep the userdata from newproxy reachable until the object + -- table is about to be garbage-collected - then the __gc hook + -- will be invoked and the destructor called + rawset(Child, '__proxy', proxy) + end --self:T( 'Inherited from ' .. Parent.ClassName ) return Child diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index e752a7d3e..3c78e6bb9 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -85,9 +85,16 @@ end -- @return #EVENT.Events function EVENT:Init( EventID, EventClass ) self:F3( { _EVENTCODES[EventID], EventClass } ) - if not self.Events[EventID] then + + if not self.Events[EventID] then self.Events[EventID] = {} + + -- Create a WEAK table to ensure that the garbage collector is cleaning the event links when the object usage is cleaned. + local Meta = {} + setmetatable( self.Events[EventID], Meta ) + Meta.__mode = "k" end + if not self.Events[EventID][EventClass] then self.Events[EventID][EventClass] = {} end diff --git a/Moose Development/Moose/Core/StateMachine.lua b/Moose Development/Moose/Core/StateMachine.lua index fd353d464..3257e8280 100644 --- a/Moose Development/Moose/Core/StateMachine.lua +++ b/Moose Development/Moose/Core/StateMachine.lua @@ -18,7 +18,7 @@ --- STATEMACHINE class -- @type STATEMACHINE --- @extends Base#BASE +-- @extends Core.Base#BASE STATEMACHINE = { ClassName = "STATEMACHINE", } @@ -34,7 +34,7 @@ function STATEMACHINE:New( options ) --local self = routines.utils.deepCopy( self ) -- Create a new self instance - assert(options.events) + --assert(options.events) --local MT = {} --setmetatable( self, MT ) @@ -46,31 +46,70 @@ function STATEMACHINE:New( options ) self.subs = {} self.endstates = {} - for _, event in ipairs(options.events or {}) do - local name = event.name - local __name = "__" .. event.name - self[name] = self[name] or self:_create_transition(name) - self[__name] = self[__name] or self:_delayed_transition(name) - self:T( "Added methods: " .. name .. ", " .. __name ) - self.events[name] = self.events[name] or { map = {} } - self:_add_to_map(self.events[name].map, event) + for _, event in pairs(options.events or {}) do + self:E({ "events", event }) + self:_eventmap( self.events, event ) end for name, callback in pairs(options.callbacks or {}) do + self:E("callbacks") self[name] = callback end for name, sub in pairs( options.subs or {} ) do + self:E("sub") self:_submap( self.subs, sub, name ) end for name, endstate in pairs( options.endstates or {} ) do + self:E("endstate") self.endstates[endstate] = endstate end return self end +function STATEMACHINE:SetInitialState( State ) + self.current = State +end + +function STATEMACHINE:AddAction( From, Event, To ) + + local event = {} + event.from = From + event.name = Event + event.to = To + + self:E( event ) + + self:_eventmap( self.events, event ) +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:AddProcess( From, Event, Process, ReturnEvents ) + + local sub = {} + sub.FromParent = From + sub.EventParent = Event + sub.fsm = Process + sub.event = "Start" + sub.ReturnEvents = ReturnEvents + + self:_submap( self.subs, sub, nil ) + + self:AddAction( From, Event, "*" ) + + return Process +end + +function STATEMACHINE:GetSubs() + + return self.options.subs +end + + function STATEMACHINE:LoadCallBacks( CallBackTable ) for name, callback in pairs( CallBackTable or {} ) do @@ -79,17 +118,29 @@ function STATEMACHINE:LoadCallBacks( CallBackTable ) end +function STATEMACHINE:_eventmap( events, event ) + + local name = event.name + local __name = "__" .. event.name + self[name] = self[name] or self:_create_transition(name) + self[__name] = self[__name] or self:_delayed_transition(name) + self:T( "Added methods: " .. name .. ", " .. __name ) + events[name] = self.events[name] or { map = {} } + self:_add_to_map( events[name].map, event ) + +end + function STATEMACHINE:_submap( subs, sub, name ) self:E( { sub = sub, name = name } ) - subs[sub.onstateparent] = subs[sub.onstateparent] or {} - subs[sub.onstateparent][sub.oneventparent] = subs[sub.onstateparent][sub.oneventparent] or {} - local Index = #subs[sub.onstateparent][sub.oneventparent] + 1 - subs[sub.onstateparent][sub.oneventparent][Index] = {} - subs[sub.onstateparent][sub.oneventparent][Index].fsm = sub.fsm - subs[sub.onstateparent][sub.oneventparent][Index].event = sub.event - subs[sub.onstateparent][sub.oneventparent][Index].returnevents = sub.returnevents -- these events need to be given to find the correct continue event ... if none given, the processing will stop. - subs[sub.onstateparent][sub.oneventparent][Index].name = name - subs[sub.onstateparent][sub.oneventparent][Index].fsmparent = self + subs[sub.FromParent] = subs[sub.FromParent] or {} + subs[sub.FromParent][sub.EventParent] = subs[sub.FromParent][sub.EventParent] or {} + local Index = #subs[sub.FromParent][sub.EventParent] + 1 + subs[sub.FromParent][sub.EventParent][Index] = {} + subs[sub.FromParent][sub.EventParent][Index].fsm = sub.fsm + subs[sub.FromParent][sub.EventParent][Index].event = sub.event + subs[sub.FromParent][sub.EventParent][Index].ReturnEvents = sub.ReturnEvents or {} -- these events need to be given to find the correct continue event ... if none given, the processing will stop. + subs[sub.FromParent][sub.EventParent][Index].name = name + subs[sub.FromParent][sub.EventParent][Index].fsmparent = self end @@ -121,11 +172,15 @@ function STATEMACHINE._handler( self, EventName, ... ) local execute = true - local subtable = self:_gosub( to, EventName ) + local subtable = self:_gosub( from, EventName ) for _, sub in pairs( subtable ) do + --if sub.nextevent then + -- self:F2( "nextevent = " .. sub.nextevent ) + -- self[sub.nextevent]( self ) + --end self:F2( "calling sub: " .. sub.event ) sub.fsm.fsmparent = self - sub.fsm.returnevents = sub.returnevents + sub.fsm.ReturnEvents = sub.ReturnEvents sub.fsm[sub.event]( sub.fsm ) execute = true end @@ -176,32 +231,30 @@ function STATEMACHINE:_create_transition( EventName ) return function( self, ... ) return self._handler( self, EventName , ... ) end end -function STATEMACHINE:_gosub( parentstate, parentevent ) +function STATEMACHINE:_gosub( ParentFrom, ParentEvent ) local fsmtable = {} - if self.subs[parentstate] and self.subs[parentstate][parentevent] then - return self.subs[parentstate][parentevent] + self:E( { ParentFrom, ParentEvent, self.subs[ParentFrom] } ) + if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then + return self.subs[ParentFrom][ParentEvent] else return {} end end -function STATEMACHINE:_isendstate( state ) - local fsmparent = self.fsmparent - if fsmparent and self.endstates[state] then - 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 and to == state then - return fsmparent, eventname - else - self:E( { "could not find parent event name for state", fromstate, to } ) - end +function STATEMACHINE:_isendstate( Current ) + local FSMParent = self.fsmparent + if FSMParent and self.endstates[Current] then + self:E( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } ) + FSMParent.current = Current + local ParentFrom = FSMParent.current + self:E( ParentFrom ) + self:E( self.ReturnEvents ) + local Event = self.ReturnEvents[Current] + self:E( { ParentFrom, Event, self.ReturnEvents } ) + if Event then + return FSMParent, Event + else + self:E( { "Could not find parent event name for state ", ParentFrom } ) end end @@ -209,6 +262,7 @@ function STATEMACHINE:_isendstate( state ) end function STATEMACHINE:_add_to_map(map, event) + self:E( { map, event } ) if type(event.from) == 'string' then map[event.from] = event.to else @@ -216,6 +270,7 @@ function STATEMACHINE:_add_to_map(map, event) map[from] = event.to end end + self:E( { map, event } ) end function STATEMACHINE:is(state) @@ -224,6 +279,7 @@ end function STATEMACHINE:can(e) local event = self.events[e] + self:E( { self.current, event } ) local to = event and event.map[self.current] or event.map['*'] return to ~= nil, to end @@ -290,7 +346,7 @@ end --- STATEMACHINE_CONTROLLABLE class -- @type STATEMACHINE_CONTROLLABLE -- @field Controllable#CONTROLLABLE Controllable --- @extends StateMachine#STATEMACHINE +-- @extends Core.StateMachine#STATEMACHINE STATEMACHINE_CONTROLLABLE = { ClassName = "STATEMACHINE_CONTROLLABLE", } diff --git a/Moose Development/Moose/Fsm/Process.lua b/Moose Development/Moose/Fsm/Process.lua index bb6c869be..5b17e1e69 100644 --- a/Moose Development/Moose/Fsm/Process.lua +++ b/Moose Development/Moose/Fsm/Process.lua @@ -6,7 +6,7 @@ -- @field Group#GROUP ProcessGroup -- @field Menu#MENU_GROUP MissionMenu -- @field #string ProcessName --- @extends StateMachine#STATEMACHINE_CONTROLLABLE +-- @extends Core.StateMachine#STATEMACHINE_CONTROLLABLE PROCESS = { ClassName = "PROCESS", NextEvent = nil, @@ -29,6 +29,7 @@ function PROCESS:New( FSMT, ProcessName, ProcessUnit ) --self.MissionMenu = Task.Mission:GetMissionMenu( self.ProcessGroup ) self.ProcessName = ProcessName + return self end @@ -67,15 +68,6 @@ function PROCESS:GetMission() return self.ProcessTask.Mission end -function PROCESS:ProcessStart() - -end - -function PROCESS:ProcessStop() - self:E("ProcessStop Base Class") - - self:EventRemoveAll() -end --- Assign the process to a @{Unit} and activate the process. -- @param #PROCESS self @@ -86,8 +78,6 @@ function PROCESS:Assign( ProcessTask, ProcessUnit ) self:SetControllable( ProcessUnit ) self:SetTask( ProcessTask ) - self:ProcessStart() - self.ProcessGroup = ProcessUnit:GetGroup() --self:Activate() diff --git a/Moose Development/Moose/Process/Account.lua b/Moose Development/Moose/Process/Account.lua index 58b4dc5e1..31514b287 100644 --- a/Moose Development/Moose/Process/Account.lua +++ b/Moose Development/Moose/Process/Account.lua @@ -124,8 +124,11 @@ do -- PROCESS_ACCOUNT -- @param #string To function PROCESS_ACCOUNT:onafterStart( ProcessUnit, Event, From, To ) + self:EventOnDead( self.EventDead ) + self:__Wait( 1 ) end + --- StateMachine callback function -- @param #PROCESS_ACCOUNT self @@ -169,6 +172,15 @@ do -- PROCESS_ACCOUNT_DEADS TargetSetUnit = nil, } + + --- Creates a new DESTROY process. + -- @param #PROCESS_ACCOUNT_DEADS self + -- @param Set#SET_UNIT TargetSetUnit + -- @param #string TaskName + function PROCESS_ACCOUNT_DEADS:Template( TargetSetUnit, TaskName ) + return { self, arg } + end + --- Creates a new DESTROY process. -- @param #PROCESS_ACCOUNT_DEADS self @@ -182,12 +194,15 @@ do -- PROCESS_ACCOUNT_DEADS self.TargetSetUnit = TargetSetUnit self.TaskName = TaskName - + return self end + + function PROCESS_ACCOUNT_DEADS:_Destructor() + self:E("_Destructor") + + self:RemoveEventsAll() - function PROCESS_ACCOUNT_DEADS:ProcessStart() - self:EventOnDead( self.EventDead ) end --- Process Events diff --git a/Moose Development/Moose/Process/Assign.lua b/Moose Development/Moose/Process/Assign.lua index 4cc51bd00..11d80cb57 100644 --- a/Moose Development/Moose/Process/Assign.lua +++ b/Moose Development/Moose/Process/Assign.lua @@ -134,6 +134,14 @@ do -- PROCESS_ASSIGN_ACCEPT } + --- 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 + function PROCESS_ASSIGN_ACCEPT:Template( TaskBriefing ) + 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 diff --git a/Moose Development/Moose/Process/Route.lua b/Moose Development/Moose/Process/Route.lua index 482f070a4..ba3451623 100644 --- a/Moose Development/Moose/Process/Route.lua +++ b/Moose Development/Moose/Process/Route.lua @@ -107,7 +107,7 @@ do -- PROCESS_ROUTE { name = 'Fail', from = '*', to = 'Failed' }, }, endstates = { - 'Arrived', 'Failed', 'Success' + 'Arrived', 'Failed' }, } @@ -150,10 +150,10 @@ do -- PROCESS_ROUTE -- @param #string Event -- @param #string From -- @param #string To - function PROCESS_ROUTE:onafterRoute( ProcessUnit, Event, From, To ) + function PROCESS_ROUTE:onbeforeRoute( ProcessUnit, Event, From, To ) if ProcessUnit:IsAlive() then - local HasArrived = self:HasArrived( ProcessUnit ) + local HasArrived = self:HasArrived( ProcessUnit ) -- Polymorphic if self.DisplayCount >= self.DisplayInterval then self:T( { HasArrived = HasArrived } ) if not HasArrived then @@ -165,7 +165,12 @@ do -- PROCESS_ROUTE end self:T( { DisplayCount = self.DisplayCount } ) - self:__Route( 1 ) + + if HasArrived then + self:__Arrive( 1 ) + else + self:__Route( 1 ) + end return HasArrived -- if false, then the event will not be executed... end @@ -189,6 +194,14 @@ do -- PROCESS_ROUTE_ZONE PROCESS_ROUTE_ZONE = { ClassName = "PROCESS_ROUTE_ZONE", } + + + --- 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 + function PROCESS_ROUTE_ZONE:Template( TargetZone ) + return { self, arg } + end --- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE. @@ -206,9 +219,15 @@ do -- PROCESS_ROUTE_ZONE --- Method override to check if the controllable has arrived. -- @param #PROCESS_ROUTE self - -- @param Controllable#CONTROLLABLE ProcessUnit + -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit -- @return #boolean function PROCESS_ROUTE_ZONE:HasArrived( ProcessUnit ) + + if ProcessUnit:IsInZone( self.TargetZone ) then + local RouteText = ProcessUnit:GetCallsign() .. ": You have arrived within the zone!" + MESSAGE:New( RouteText, self.DisplayTime, self.DisplayCategory ):ToGroup( ProcessUnit:GetGroup() ) + end + return ProcessUnit:IsInZone( self.TargetZone ) end diff --git a/Moose Development/Moose/Process/Smoke.lua b/Moose Development/Moose/Process/Smoke.lua index 0df9b4c93..69e926efb 100644 --- a/Moose Development/Moose/Process/Smoke.lua +++ b/Moose Development/Moose/Process/Smoke.lua @@ -144,13 +144,20 @@ do -- PROCESS_SMOKE_TARGETS_ZONE ClassName = "PROCESS_SMOKE_TARGETS_ZONE", } - function PROCESS_SMOKE_TARGETS_ZONE:ProcessStop() - self:E("ProcessStop Detailed") + function PROCESS_SMOKE_TARGETS_ZONE:_Destructor() + self:E("_Destructor") self.Menu:Remove() - + self:EventRemoveAll() 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. + -- @param #PROCESS_SMOKE_TARGETS_ZONE self + -- @param Set#SET_UNIT TargetSetUnit + -- @param Zone#ZONE_BASE TargetZone + function PROCESS_SMOKE_TARGETS_ZONE:Template( TargetSetUnit, TargetZone ) + return { self, arg } + 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. -- @param #PROCESS_SMOKE_TARGETS_ZONE self diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 62bc19ca8..9a5c812d8 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -9,8 +9,8 @@ -- * @{#TASK_BASE.AssignToGroup}():Assign a task to a group (of players). -- * @{#TASK_BASE.AddProcess}():Add a @{Process} to a task. -- * @{#TASK_BASE.RemoveProcesses}():Remove a running @{Process} from a running task. --- * @{#TASK_BASE.AddStateMachine}():Add a @{StateMachine} to a task. --- * @{#TASK_BASE.RemoveStateMachines}():Remove @{StateMachine}s from a task. +-- * @{#TASK_BASE.SetStateMachine}():Set a @{StateMachine} to a task. +-- * @{#TASK_BASE.RemoveStateMachine}():Remove @{StateMachine} from a task. -- * @{#TASK_BASE.HasStateMachine}():Enquire if the task has a @{StateMachine} -- * @{#TASK_BASE.AssignToUnit}(): Assign a task to a unit. (Needs to be implemented in the derived classes from @{#TASK_BASE}. -- * @{#TASK_BASE.UnAssignFromUnit}(): Unassign the task from a unit. @@ -69,13 +69,13 @@ TASK_BASE = { --- Instantiates a new TASK_BASE. Should never be used. Interface Class. -- @param #TASK_BASE self --- @param Mission#MISSION The mission wherein the Task is registered. --- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. +-- @param Mission#MISSION Mission The mission wherein the Task is registered. +-- @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, SetGroup, TaskName, TaskType, TaskCategory ) +function TASK_BASE:New( Mission, SetGroupAssign, TaskName, TaskType, TaskCategory ) local self = BASE:Inherit( self, BASE:New() ) self:E( "New TASK " .. TaskName ) @@ -84,7 +84,7 @@ function TASK_BASE:New( Mission, SetGroup, TaskName, TaskType, TaskCategory ) self.Fsm = {} self.Mission = Mission - self.SetGroup = SetGroup + self.SetGroup = SetGroupAssign self:SetCategory( TaskCategory ) self:SetType( TaskType ) @@ -92,6 +92,8 @@ function TASK_BASE:New( Mission, SetGroup, TaskName, TaskType, TaskCategory ) self:SetID( Mission:GetNextTaskID( self ) ) -- The Mission orchestrates the task sequences .. self.TaskBriefing = "You are assigned to the task: " .. self.TaskName .. "." + + self.FsmTemplate = self.FsmTemplate or STATEMACHINE_TASK:New( {}, self ) return self end @@ -109,6 +111,10 @@ function TASK_BASE:CleanUp() return nil end +function TASK_BASE:GetFsmTemplate() + + return self.FsmTemplate +end --- Assign the @{Task}to a @{Group}. -- @param #TASK_BASE self @@ -128,6 +134,7 @@ function TASK_BASE:AssignToGroup( TaskGroup ) for UnitID, UnitData in pairs( TaskUnits ) do local TaskUnit = UnitData -- Unit#UNIT local PlayerName = TaskUnit:GetPlayerName() + self:E(PlayerName) if PlayerName ~= nil or PlayerName ~= "" then self:AssignToUnit( TaskUnit ) end @@ -136,6 +143,65 @@ function TASK_BASE:AssignToGroup( TaskGroup ) return self end +--- Assign the @{Task} to an alive @{Unit}. +-- @param #TASK_BASE self +-- @param Unit#UNIT TaskUnit +-- @return #TASK_BASE self +function TASK_BASE:AssignToUnit( TaskUnit ) + self:F( TaskUnit:GetName() ) + + -- 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 ) + FsmUnit:Assign( self, TaskUnit ) + + -- Assign each FsmSub in FsmUnit to the TaskUnit. + -- (This is not done during the copy). + for FsmSubID, FsmSub in ipairs( FsmUnit:GetSubs() ) do + self:E( { "Sub ID", FsmSub:GetClassNameAndID() } ) + FsmSub:Assign( self, TaskUnit ) + 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 + + FsmUnit:SetInitialState( "Planned" ) + FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. + + return self +end + +--- UnAssign the @{Task} from an alive @{Unit}. +-- @param #TASK_BASE self +-- @param Unit#UNIT TaskUnit +-- @return #TASK_BASE self +function TASK_BASE:UnAssignFromUnit( TaskUnitName ) + self:F( TaskUnitName ) + + if self:HasStateMachine( TaskUnitName ) == true then + self:E("RemoveStateMachines") + self:RemoveStateMachine( TaskUnitName ) + end + + return self +end + + + --- Send the briefng message of the @{Task} to the assigned @{Group}s. -- @param #TASK_BASE self function TASK_BASE:SendBriefingToAssignedGroups() @@ -186,33 +252,6 @@ function TASK_BASE:IsAssignedToGroup( TaskGroup ) return false end ---- Assign the @{Task} to an alive @{Unit}. --- @param #TASK_BASE self --- @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( TaskUnitName ) - self:F( TaskUnitName ) - - if self:HasStateMachine( TaskUnitName ) == true then - self:E("RemoveStateMachines") - self:RemoveStateMachines( TaskUnitName ) - self:E("RemoveProcesses") - self:RemoveProcesses( TaskUnitName ) - end - - return self -end - --- Set the menu options of the @{Task} to all the groups in the SetGroup. -- @param #TASK_BASE self -- @return #TASK_BASE self @@ -406,31 +445,6 @@ function TASK_BASE:GetTaskName() end ---- This is the key worker function for the class. Instantiate a new Process based on the ProcessName to @{Task} and assign it to the ProcessUnit. --- @param #TASK_BASE self --- @param Unit#UNIT ProcessUnit The unit to which the process should be assigned. --- @param #string ProcessName The name of the Process. --- @return Process#PROCESS The Process that was added. -function TASK_BASE:AssignProcess( ProcessUnit, ProcessName ) - self:F( { ProcessName } ) - local ProcessUnitName = ProcessUnit:GetName() - - -- Create the Process instance base on the ProcessClasses collection assigned to the Task - local ProcessTemplate, ProcessArguments - ProcessTemplate = self:GetProcessTemplate( ProcessName ) - - self:E( "Deepcopy" ) - -- This statement copies the process template assigned to the task and creates a new process. - local Process = UTILS.DeepCopy( ProcessTemplate ) -- Fsm.Process#PROCESS - Process:Assign( self, ProcessUnit ) - - self.Processes = self.Processes or {} - self.Processes[ProcessUnitName] = self.Processes[ProcessUnitName] or {} - - self.Processes[ProcessUnitName][ProcessName] = Process - - return Process -end --- Get the default or currently assigned @{Process} template with key ProcessName. @@ -445,35 +459,6 @@ function TASK_BASE:GetProcessTemplate( ProcessName ) 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. --- @param #TASK_BASE self --- @param #string ProcessName --- @param Process#PROCESS ProcessTemplate --- @return Process#PROCESS -function TASK_BASE:SetProcessTemplate( ProcessName, ProcessTemplate ) - - self.ProcessClasses[ProcessName] = ProcessTemplate - - return ProcessTemplate -end - - ---- Remove Processes from @{Task} with key @{Unit} --- @param #TASK_BASE self --- @param #string TaskUnitName --- @return #TASK_BASE self -function TASK_BASE:RemoveProcesses( TaskUnitName ) - self:E( TaskUnitName ) - - for ProcessID, ProcessData in pairs( self.Processes[TaskUnitName] ) do - local Process = ProcessData -- Process.Process#PROCESS - Process:ProcessStop() - Process = nil - self.Processes[TaskUnitName][ProcessID] = nil - self:E( self.Processes[TaskUnitName][ProcessID] ) - end - self.Processes[TaskUnitName] = nil -end --- Fail processes from @{Task} with key @{Unit} -- @param #TASK_BASE self @@ -491,10 +476,10 @@ end -- @param #TASK_BASE self -- @param Unit#UNIT TaskUnit -- @return #TASK_BASE self -function TASK_BASE:AddStateMachine( TaskUnit, Fsm ) +function TASK_BASE:SetStateMachine( TaskUnit, Fsm ) local TaskUnitName = TaskUnit:GetName() - self.Fsm[TaskUnitName] = self.Fsm[TaskUnitName] or {} - self.Fsm[TaskUnitName][#self.Fsm[TaskUnitName]+1] = Fsm + self.Fsm[TaskUnitName] = Fsm + return Fsm end @@ -502,14 +487,10 @@ end -- @param #TASK_BASE self -- @param #string TaskUnitName -- @return #TASK_BASE self -function TASK_BASE:RemoveStateMachines( TaskUnitName ) +function TASK_BASE:RemoveStateMachine( TaskUnitName ) - for _, Fsm in pairs( self.Fsm[TaskUnitName] ) do - Fsm = nil - self.Fsm[TaskUnitName][_] = nil - self:E( self.Fsm[TaskUnitName][_] ) - end self.Fsm[TaskUnitName] = nil + collectgarbage() end --- Checks if there is a FiniteStateMachine assigned to @{Unit} for @{Task} @@ -875,7 +856,7 @@ function TASK_BASE:OnStateChange( TaskUnit, Fsm, Event, From, To ) MESSAGE:New( "Task " .. self.TaskName .. " : " .. Event .. " changed to state " .. To, 15 ):ToAll() end - self:E( { Event, From, To } ) + self:T2( { Event, From, To } ) self:SetState( self, "State", To ) if self.Scores[To] then diff --git a/Moose Development/Moose/Tasking/Task_SEAD.lua b/Moose Development/Moose/Tasking/Task_SEAD.lua index bb2669b5c..bd75b717f 100644 --- a/Moose Development/Moose/Tasking/Task_SEAD.lua +++ b/Moose Development/Moose/Tasking/Task_SEAD.lua @@ -67,8 +67,7 @@ do -- TASK_SEAD return nil end - - + --- Assign the @{Task} to a @{Unit}. -- @param #TASK_SEAD self @@ -85,7 +84,7 @@ do -- TASK_SEAD local FSMT = { initial = 'None', events = { - { name = 'Next', from = 'None', to = 'Planned' }, + { name = 'Start', from = 'None', to = 'Planned' }, { name = 'Next', from = 'Planned', to = 'Assigned' }, { name = 'Reject', from = 'Planned', to = 'Rejected' }, { name = 'Next', from = 'Assigned', to = 'Success' }, 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 748afbd32..c56db1af5 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,4 +1,6 @@ +env.info( "Lua Version = " .. _VERSION ) + local Mission = MISSION:New( 'SEAD Targets', "Strategic", "SEAD the enemy", coalition.side.RED ) local Scoring = SCORING:New( "SEAD" ) @@ -9,23 +11,31 @@ local TargetSet = SET_UNIT:New():FilterPrefixes( "US Hawk SR" ):FilterOnce() local TargetZone = ZONE:New( "Target Zone" ) -local TaskSEAD = TASK_SEAD - :New( Mission, SEADSet, "SEAD Radars", TargetSet, TargetZone ) +local TaskSEAD = TASK_BASE:New( Mission, SEADSet, "SEAD Radars", "A2G", "SEAD" ) -- Tasking.Task#TASK_BASE + --:New( Mission, SEADSet, "SEAD Radars", TargetSet, TargetZone ) -TaskSEAD:AddScore( "Success", "Destroyed all target radars", 250 ) -TaskSEAD:AddScore( "Failed", "Failed to destroy all target radars", -100 ) +local FsmSEAD = TaskSEAD:GetFsmTemplate() -local AssignProcess = TaskSEAD:SetProcessTemplate( "ASSIGN", PROCESS_ASSIGN_MENU_ACCEPT:New( "SEAD", "Hello World" ) ) -AssignProcess:AddScore( TaskSEAD, "Assign", "You are assigned to the task", 10 ) +FsmSEAD:AddProcess( "Planned", "Accept", PROCESS_ASSIGN_ACCEPT:New( "SEAD the Area" ), { Assigned = "Route", Rejected = "Eject" } ) +FsmSEAD:AddProcess( "Assigned", "Route", PROCESS_ROUTE_ZONE:New( TargetZone, 3000 ), { Arrived = "Update" } ) +FsmSEAD:AddAction ( "Rejected", "Eject", "Planned" ) +FsmSEAD:AddAction ( "Arrived", "Update", "Updated" ) +FsmSEAD:AddProcess( "Updated", "Account", PROCESS_ACCOUNT_DEADS:New( TargetSet, "SEAD" ), { Destroyed = "Success" } ) +FsmSEAD:AddProcess( "Updated", "Smoke", PROCESS_SMOKE_TARGETS_ZONE:New( TargetSet, TargetZone ) ) +FsmSEAD:AddAction ( "Destroyed", "Success", "Success" ) +FsmSEAD:AddAction ( "Failed", "Fail", "Failed" ) -local AccountProcess = TaskSEAD:SetProcessTemplate( "ACCOUNT", PROCESS_ACCOUNT_DEADS:New( TargetSet, "SEAD" ) ) -AccountProcess:AddScore( TaskSEAD, "Account", "destroyed a radar", 25 ) -AccountProcess:AddScore( TaskSEAD, "Failed", "failed to destroy a radar", -100 ) +--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( "Smoke", "Failed", "failed to destroy a radar", -100 ) ---local SmokeProcess = TaskSEAD:SetProcessTemplate( "SMOKE", PROCESS_SMOKE_TARGETS_ZONE:New( TargetSet, TargetZone ) ) ---SmokeProcess:SetAttackGroup( GROUP:FindByName( "SmokeGroup" ), "Watchdog" ) ---SmokeProcess:AddScore( TaskSEAD, "Account", "destroyed a radar", 25 ) ---SmokeProcess:AddScore( TaskSEAD, "Failed", "failed to destroy a radar", -100 ) +function FsmSEAD:onenterUpdated( TaskUnit ) + TaskSEAD:Account() + TaskSEAD:Smoke() +end +-- Needs to be checked, should not be necessary ... TaskSEAD:AssignToGroup( SEADSet:Get( "Test SEAD" ) ) +Mission:AddTask( TaskSEAD ) 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 208db4352a7dd1e4fd5ac7b8e3c942ff13e12b2b..ef898521d5f7e903f9c38c0c61926728244a9934 100644 GIT binary patch delta 13285 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 O5BX1Wf!^f5z5fN%T65|E delta 13554 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

=SnUV2k|0p}#-R9Y$-vM`RcwJ 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 Nkd`vr7X~e$J^;;VWfuSd delta 17169 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 From 4816cd2c57e1a85b2e76cd1c56cd279b33f54a1f Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 22 Nov 2016 12:46:28 +0100 Subject: [PATCH 2/2] Got tasking working again now, with the NEW API set!!! This is really, really great! Now document all, and optimize the code. And start video making. And re-test the test mission, all... And PUBLISH!!! --- Moose Development/Moose/Core/StateMachine.lua | 169 ++++++++++++------ Moose Development/Moose/Fsm/Process.lua | 54 +----- .../Moose/Functional/Scoring.lua | 20 ++- Moose Development/Moose/Process/Account.lua | 7 +- Moose Development/Moose/Tasking/Task.lua | 71 ++++---- .../Moose_Test_Task_SEAD.lua | 17 +- .../Moose_Test_Task_SEAD.miz | Bin 25960 -> 25962 bytes 7 files changed, 185 insertions(+), 153 deletions(-) diff --git a/Moose Development/Moose/Core/StateMachine.lua b/Moose Development/Moose/Core/StateMachine.lua index 3257e8280..1344d8c03 100644 --- a/Moose Development/Moose/Core/StateMachine.lua +++ b/Moose Development/Moose/Core/StateMachine.lua @@ -41,6 +41,7 @@ function STATEMACHINE:New( options ) --self.__index = self self.options = options + self.options.subs = self.options.subs or {} self.current = options.initial or 'none' self.events = {} self.subs = {} @@ -96,6 +97,8 @@ function STATEMACHINE:AddProcess( From, Event, Process, ReturnEvents ) sub.fsm = Process sub.event = "Start" sub.ReturnEvents = ReturnEvents + + self.options.subs[Event] = sub self:_submap( self.subs, sub, nil ) @@ -145,26 +148,27 @@ end function STATEMACHINE:_call_handler(handler, params) - if handler then - return handler( self, unpack(params) ) + if self[handler] then + self:E( "Calling " .. handler ) + return self[handler]( self, unpack(params) ) end end function STATEMACHINE._handler( self, EventName, ... ) - self:F( { EventName, ... } ) + self:E( { EventName, ... } ) - local can, to = self:can(EventName) - self:T( { EventName, can, to } ) + local can, to = self:can( EventName ) + self:E( { EventName, can, to } ) local ReturnValues = nil if can then local from = self.current - local params = { ..., EventName, from, to } + local params = { EventName, from, to, ... } - if self:_call_handler(self["onbefore" .. EventName], params) == false - or self:_call_handler(self["onleave" .. from], params) == false then + if self:_call_handler("onbefore" .. EventName, params) == false + or self:_call_handler("onleave" .. from, params) == false then return false end @@ -188,28 +192,28 @@ function STATEMACHINE._handler( self, EventName, ... ) local fsmparent, event = self:_isendstate( to ) if fsmparent and event then self:F2( { "end state: ", fsmparent, event } ) - self:_call_handler(self["onenter" .. to] or self["on" .. to], params) - self:_call_handler(self["onafter" .. EventName] or self["on" .. EventName], params) - self:_call_handler(self["onstatechange"], params) + self:_call_handler("onenter" .. to, params) + self:_call_handler("onafter" .. EventName, params) + self:_call_handler("onstatechange", params) fsmparent[event]( fsmparent ) execute = false end if execute then self:T3( { onenter = "onenter" .. to, callback = self["onenter" .. to] } ) - self:_call_handler(self["onenter" .. to] or self["on" .. to], params) + self:_call_handler("onenter" .. to, params) self:T3( { On = "OnBefore" .. to, callback = self["OnBefore" .. to] } ) - if ( self:_call_handler(self["OnBefore" .. to], params ) ~= false ) then + if ( self:_call_handler("OnBefore" .. to, params ) ~= false ) then self:T3( { onafter = "onafter" .. EventName, callback = self["onafter" .. EventName] } ) - self:_call_handler(self["onafter" .. EventName] or self["on" .. EventName], params) + self:_call_handler("onafter" .. EventName, params) self:T3( { On = "OnAfter" .. to, callback = self["OnAfter" .. to] } ) - ReturnValues = self:_call_handler(self["OnAfter" .. to], params ) + ReturnValues = self:_call_handler("OnAfter" .. to, params ) end - self:_call_handler(self["onstatechange"], params) + self:_call_handler("onstatechange", params) end return ReturnValues @@ -233,8 +237,8 @@ end function STATEMACHINE:_gosub( ParentFrom, ParentEvent ) local fsmtable = {} - self:E( { ParentFrom, ParentEvent, self.subs[ParentFrom] } ) if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then + self:E( { ParentFrom, ParentEvent, self.subs[ParentFrom] } ) return self.subs[ParentFrom][ParentEvent] else return {} @@ -308,40 +312,6 @@ function STATEMACHINE:todot(filename) 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 --- @param #table FSMT --- @param Task#TASK_BASE Task --- @param Unit#UNIT TaskUnit --- @return #STATEMACHINE_TASK -function STATEMACHINE_TASK:New( FSMT, Task, TaskUnit ) - - local self = BASE:Inherit( self, STATEMACHINE:New( FSMT ) ) -- StateMachine#STATEMACHINE_PROCESS - - self["onstatechange"] = Task.OnStateChange - self["onAssigned"] = Task.OnAssigned - self["onSuccess"] = Task.OnSuccess - self["onFailed"] = Task.OnFailed - - self.Task = Task - self.TaskUnit = TaskUnit - - return self -end - -function STATEMACHINE_TASK:_call_handler( handler, params ) - if handler then - return handler( self.Task, self.TaskUnit, unpack( params ) ) - end -end --- STATEMACHINE_CONTROLLABLE class -- @type STATEMACHINE_CONTROLLABLE @@ -385,14 +355,16 @@ function STATEMACHINE_CONTROLLABLE:GetControllable() end function STATEMACHINE_CONTROLLABLE:_call_handler( handler, params ) - if handler then - return handler( self, self.Controllable, unpack( params ) ) + if self[handler] then + self:E( "Calling " .. handler ) + return self[handler]( self, self.Controllable, unpack( params ) ) end end --- STATEMACHINE_PROCESS class -- @type STATEMACHINE_PROCESS -- @field Process#PROCESS Process +-- @field Tasking.Task#TASK_BASE Task -- @extends Core.StateMachine#STATEMACHINE_CONTROLLABLE STATEMACHINE_PROCESS = { ClassName = "STATEMACHINE_PROCESS", @@ -408,6 +380,92 @@ function STATEMACHINE_PROCESS:New( FSMT ) return self end +--- Sets the task of the process. +-- @param #PROCESS self +-- @param Tasking.Task#TASK_BASE Task +-- @return #PROCESS +function STATEMACHINE_PROCESS:SetTask( Task ) + + self.Task = Task + + return self +end + +--- Gets the task of the process. +-- @param #PROCESS self +-- @return Task#TASK_BASE +function STATEMACHINE_PROCESS:GetTask() + + return self.Task +end + +--- Gets the mission of the process. +-- @param #PROCESS self +-- @return Mission#MISSION +function STATEMACHINE_PROCESS:GetMission() + + return self.Task.Mission +end + + +--- Assign the process to a @{Unit} and activate the process. +-- @param #PROCESS self +-- @param Task.Tasking#TASK_BASE Task +-- @param Wrapper.Unit#UNIT ProcessUnit +-- @return #PROCESS self +function STATEMACHINE_PROCESS:Assign( Task, ProcessUnit ) + self:E( { Task, ProcessUnit } ) + + self:SetControllable( ProcessUnit ) + self:SetTask( Task ) + + self.ProcessGroup = ProcessUnit:GetGroup() + + --self:Activate() + + return self +end + +function STATEMACHINE_PROCESS:onenterAssigned( ProcessUnit ) + + self.Task:Assign() +end + +function STATEMACHINE_PROCESS:onenterSuccess( ProcessUnit ) + + self.Task:Success() +end + +--- STATEMACHINE_TASK class +-- @type STATEMACHINE_TASK +-- @field Task#TASK_BASE Task +-- @extends Core.StateMachine#STATEMACHINE +STATEMACHINE_TASK = { + ClassName = "STATEMACHINE_TASK", +} + +--- Creates a new STATEMACHINE_TASK object. +-- @param #STATEMACHINE_TASK self +-- @param #table FSMT +-- @param Task#TASK_BASE Task +-- @param Unit#UNIT TaskUnit +-- @return #STATEMACHINE_TASK +function STATEMACHINE_TASK:New( FSMT ) + + local self = BASE:Inherit( self, STATEMACHINE_CONTROLLABLE:New( FSMT ) ) -- Core.StateMachine#STATEMACHINE_TASK + + self["onstatechange"] = self.OnStateChange + + return self +end + +function STATEMACHINE_TASK:_call_handler( handler, params ) + if self[handler] then + self:E( "Calling " .. handler ) + return self[handler]( self, unpack( params ) ) + end +end + do -- STATEMACHINE_SET --- STATEMACHINE_SET class @@ -452,8 +510,9 @@ function STATEMACHINE_SET:Get() end function STATEMACHINE_SET:_call_handler( handler, params ) - if handler then - return handler( self, self.Set, unpack( params ) ) + if self[handler] then + self:E( "Calling " .. handler ) + return self[handler]( self, self.Set, unpack( params ) ) end end diff --git a/Moose Development/Moose/Fsm/Process.lua b/Moose Development/Moose/Fsm/Process.lua index 5b17e1e69..4b180ea1b 100644 --- a/Moose Development/Moose/Fsm/Process.lua +++ b/Moose Development/Moose/Fsm/Process.lua @@ -19,7 +19,7 @@ PROCESS = { -- @param Unit#UNIT ProcessUnit (Optional) If provided, it defines the UNIT for which the process is running. -- @return #PROCESS function PROCESS:New( FSMT, ProcessName, ProcessUnit ) - local self = BASE:Inherit( self, STATEMACHINE_CONTROLLABLE:New( FSMT, ProcessUnit ) ) + local self = BASE:Inherit( self, STATEMACHINE_PROCESS:New( FSMT, ProcessUnit ) ) self:F() if ProcessUnit then @@ -41,63 +41,20 @@ function PROCESS:GetGroup() return self.ProcessGroup end ---- Sets the task of the process. --- @param #PROCESS self --- @param Tasking.Task#TASK_BASE ProcessTask --- @return #PROCESS -function PROCESS:SetTask( ProcessTask ) - self.ProcessTask = ProcessTask - - return self -end - ---- Gets the task of the process. --- @param #PROCESS self --- @return Task#TASK_BASE -function PROCESS:GetTask() - - return self.ProcessTask -end - ---- Gets the mission of the process. --- @param #PROCESS self --- @return Mission#MISSION -function PROCESS:GetMission() - - return self.ProcessTask.Mission -end - - ---- Assign the process to a @{Unit} and activate the process. --- @param #PROCESS self --- @param Unit#UNIT ProcessUnit --- @return #PROCESS self -function PROCESS:Assign( ProcessTask, ProcessUnit ) - - self:SetControllable( ProcessUnit ) - self:SetTask( ProcessTask ) - - self.ProcessGroup = ProcessUnit:GetGroup() - --self:Activate() - - return self -end --- Adds a score for the PROCESS to be achieved. -- @param #PROCESS self --- @param Task#TASK_BASE Task The task for which the process needs to account score. -- @param #string ProcessStatus 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 #PROCESS self -function PROCESS:AddScore( Task, ProcessStatus, ScoreText, Score ) +function PROCESS:AddScore( ProcessStatus, ScoreText, Score ) self:F2( { ProcessStatus, ScoreText, Score } ) self.Scores[ProcessStatus] = self.Scores[ProcessStatus] or {} self.Scores[ProcessStatus].ScoreText = ScoreText self.Scores[ProcessStatus].Score = Score - self.Scores[ProcessStatus].Task = Task return self end @@ -108,17 +65,18 @@ end -- @param #string Event -- @param #string From -- @param #string To -function PROCESS:OnStateChange( ProcessUnit, Event, From, To ) - self:E( { self.ProcessName, Event, From, To, ProcessUnit.UnitName } ) +function PROCESS:onstatechange( ProcessUnit, Event, From, To, Dummy ) + self:E( { ProcessUnit, Event, From, To, Dummy } ) if self:IsTrace() then MESSAGE:New( "Process " .. self.ProcessName .. " : " .. Event .. " changed to state " .. To, 15 ):ToAll() end + self:E( self.Scores[To] ) -- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects... if self.Scores[To] then - local Task = self.Scores[To].Task + local Task = self.Task local Scoring = Task:GetScoring() if Scoring then Scoring:_AddMissionTaskScore( Task.Mission, ProcessUnit, self.Scores[To].ScoreText, self.Scores[To].Score ) diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 7144a07b6..dbfddb868 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -275,18 +275,20 @@ function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score ) local MissionName = Mission:GetName() self:F( { Mission:GetName(), PlayerUnit.UnitName, PlayerName, Text, Score } ) + + local PlayerData = self.Players[PlayerName] - if not self.Players[PlayerName].Mission[MissionName] then - self.Players[PlayerName].Mission[MissionName] = {} - self.Players[PlayerName].Mission[MissionName].ScoreTask = 0 - self.Players[PlayerName].Mission[MissionName].ScoreMission = 0 + if not PlayerData.Mission[MissionName] then + PlayerData.Mission[MissionName] = {} + PlayerData.Mission[MissionName].ScoreTask = 0 + PlayerData.Mission[MissionName].ScoreMission = 0 end self:T( PlayerName ) - self:T( self.Players[PlayerName].Mission[MissionName] ) + self:T( PlayerData.Mission[MissionName] ) - self.Players[PlayerName].Score = self.Players[PlayerName].Score + Score - self.Players[PlayerName].Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score + PlayerData.Score = self.Players[PlayerName].Score + Score + PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score MESSAGE:New( "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " .. Score .. " task score!", @@ -306,10 +308,12 @@ function SCORING:_AddMissionScore( Mission, Text, Score ) local MissionName = Mission:GetName() - self:F( { Mission, Text, Score } ) + self:E( { Mission, Text, Score } ) + self:E( self.Players ) for PlayerName, PlayerData in pairs( self.Players ) do + self:E( PlayerData ) if PlayerData.Mission[MissionName] then PlayerData.Score = PlayerData.Score + Score diff --git a/Moose Development/Moose/Process/Account.lua b/Moose Development/Moose/Process/Account.lua index 31514b287..cd2b94eb3 100644 --- a/Moose Development/Moose/Process/Account.lua +++ b/Moose Development/Moose/Process/Account.lua @@ -96,10 +96,10 @@ do -- PROCESS_ACCOUNT { name = 'Report', from = '*', to = 'Report' }, { name = 'Event', from = '*', to = 'Account' }, { name = 'More', from = 'Account', to = 'Wait' }, - { name = 'NoMore', from = 'Account', to = 'Success' }, + { name = 'NoMore', from = 'Account', to = 'Accounted' }, { name = 'Fail', from = '*', to = 'Failed' }, }, - endstates = { 'Success', 'Failed' } + endstates = { 'Accounted', 'Failed' } } -- Inherits from BASE @@ -214,6 +214,7 @@ do -- PROCESS_ACCOUNT_DEADS -- @param #string From -- @param #string To function PROCESS_ACCOUNT_DEADS:onenterReport( ProcessUnit, Event, From, To ) + self:E( { ProcessUnit, Event, From, To } ) local TaskGroup = ProcessUnit:GetGroup() MESSAGE:New( "Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed.", 5, "HQ" ):ToGroup( TaskGroup ) @@ -226,7 +227,7 @@ do -- PROCESS_ACCOUNT_DEADS -- @param #string Event -- @param #string From -- @param #string To - function PROCESS_ACCOUNT_DEADS:onenterAccount( ProcessUnit, EventData, Event, From, To ) + function PROCESS_ACCOUNT_DEADS:onenterAccount( ProcessUnit, Event, From, To, EventData ) self:T( { ProcessUnit, EventData, Event, From, To } ) self:T({self.Controllable}) diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 9a5c812d8..006f74aea 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -77,7 +77,14 @@ TASK_BASE = { -- @return #TASK_BASE self function TASK_BASE:New( Mission, SetGroupAssign, TaskName, TaskType, TaskCategory ) - local self = BASE:Inherit( self, BASE:New() ) + + local self = BASE:Inherit( self, STATEMACHINE_TASK:New( {} ) ) + + self:SetInitialState( "Planned" ) + self:AddAction( "Planned", "Assign", "Assigned" ) + self:AddAction( "Assigned", "Success", "Success" ) + self:AddAction( "*", "Fail", "Failed" ) + self:E( "New TASK " .. TaskName ) self.Processes = {} @@ -93,7 +100,8 @@ function TASK_BASE:New( Mission, SetGroupAssign, TaskName, TaskType, TaskCategor self.TaskBriefing = "You are assigned to the task: " .. self.TaskName .. "." - self.FsmTemplate = self.FsmTemplate or STATEMACHINE_TASK:New( {}, self ) + self.FsmTemplate = self.FsmTemplate or STATEMACHINE_PROCESS:New( {} ) + self.FsmTemplate:SetTask( self ) return self end @@ -158,9 +166,10 @@ function TASK_BASE:AssignToUnit( TaskUnit ) -- Assign each FsmSub in FsmUnit to the TaskUnit. -- (This is not done during the copy). - for FsmSubID, FsmSub in ipairs( FsmUnit:GetSubs() ) do - self:E( { "Sub ID", FsmSub:GetClassNameAndID() } ) - FsmSub:Assign( self, TaskUnit ) + self:E(FsmUnit:GetSubs()) + for FsmSubID, FsmSub in pairs( FsmUnit:GetSubs() ) do + self:E( { "Sub ID", FsmSub.fsm:GetClassNameAndID(), FsmSubID } ) + FsmSub.fsm:Assign( self, TaskUnit ) end @@ -590,7 +599,7 @@ end --- Gets the Scoring of the task -- @param #TASK_BASE self --- @return Scoring#SCORING Scoring +-- @return Functional.Scoring#SCORING Scoring function TASK_BASE:GetScoring() return self.Mission:GetScoring() end @@ -766,7 +775,7 @@ end -- @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 #TASK_BASE self -function TASK_BASE:AddScore( TaskStatus, ScoreText, Score ) +function TASK_BASE:AddScoreTask( TaskStatus, ScoreText, Score ) self:F2( { TaskStatus, ScoreText, Score } ) self.Scores[TaskStatus] = self.Scores[TaskStatus] or {} @@ -775,26 +784,35 @@ function TASK_BASE:AddScore( TaskStatus, ScoreText, Score ) return self end +--- Adds a score for the TASK to be achieved. +-- @param #TASK_BASE self +-- @param #string TaskStatus is the status of the TASK when the score needs to be given. +-- @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 #TASK_BASE self +function TASK_BASE:AddScoreProcess( Event, State, ScoreText, Score ) + self:F2( { State, ScoreText, Score } ) + + + self:E( self:GetFsmTemplate():GetSubs()[Event].fsm ) + local Process = self:GetFsmTemplate():GetSubs()[Event].fsm + + Process:AddScore( State, ScoreText, Score ) + + return self +end + --- StateMachine callback function for a TASK -- @param #TASK_BASE self --- @param Unit#UNIT TaskUnit -- @param StateMachine#STATEMACHINE_TASK Fsm -- @param #string Event -- @param #string From -- @param #string To -- @param Event#EVENTDATA Event -function TASK_BASE:OnAssigned( TaskUnit, Fsm, Event, From, To ) +function TASK_BASE:onenterAssigned( Fsm, Event, From, To ) self:E("Assigned") - - local TaskGroup = TaskUnit:GetGroup() - - TaskGroup:Message( self.TaskBriefing, 20 ) - - self:RemoveMenuForGroup( TaskGroup ) - self:SetAssignedMenuForGroup( TaskGroup ) - end @@ -806,19 +824,9 @@ end -- @param #string From -- @param #string To -- @param Event#EVENTDATA Event -function TASK_BASE:OnSuccess( TaskUnit, Fsm, Event, From, To ) +function TASK_BASE:onenterSuccess( TaskUnit, Fsm, Event, From, To ) self:E("Success") - - self:UnAssignFromGroups() - self:RemoveMenu() - - local TaskGroup = TaskUnit:GetGroup() - - self:StateSuccess() - - -- The task has become successful, the event catchers can be cleaned. - self:EventRemoveAll() end --- StateMachine callback function for a TASK @@ -850,17 +858,18 @@ end -- @param #string From -- @param #string To -- @param Event#EVENTDATA Event -function TASK_BASE:OnStateChange( TaskUnit, Fsm, Event, From, To ) +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:T2( { Event, From, To } ) - self:SetState( self, "State", To ) + 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 Scoring:_AddMissionScore( self.Mission, self.Scores[To].ScoreText, self.Scores[To].Score ) end 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 c56db1af5..1aa1cf8bb 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 @@ -20,19 +20,20 @@ FsmSEAD:AddProcess( "Planned", "Accept", PROCESS_ASSIGN_ACCEPT:New( "SEAD t FsmSEAD:AddProcess( "Assigned", "Route", PROCESS_ROUTE_ZONE:New( TargetZone, 3000 ), { Arrived = "Update" } ) FsmSEAD:AddAction ( "Rejected", "Eject", "Planned" ) FsmSEAD:AddAction ( "Arrived", "Update", "Updated" ) -FsmSEAD:AddProcess( "Updated", "Account", PROCESS_ACCOUNT_DEADS:New( TargetSet, "SEAD" ), { Destroyed = "Success" } ) +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 ( "Destroyed", "Success", "Success" ) +FsmSEAD:AddAction ( "Accounted", "Success", "Success" ) FsmSEAD:AddAction ( "Failed", "Fail", "Failed" ) ---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( "Smoke", "Failed", "failed to destroy a radar", -100 ) +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 ) function FsmSEAD:onenterUpdated( TaskUnit ) - TaskSEAD:Account() - TaskSEAD:Smoke() + self:E( { self } ) + self:Account() + self:Smoke() end -- Needs to be checked, should not be necessary ... 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 ef898521d5f7e903f9c38c0c61926728244a9934..eec01869745cbc071efc30a21d6dd06aeb494734 100644 GIT binary patch delta 11281 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