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 208db4352..ef898521d 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 diff --git a/Moose Training/Presentations/DCS World - MOOSE - Tasking - SEAD.pptx b/Moose Training/Presentations/DCS World - MOOSE - Tasking - SEAD.pptx index 05534866a..1b54eb9a5 100644 Binary files a/Moose Training/Presentations/DCS World - MOOSE - Tasking - SEAD.pptx and b/Moose Training/Presentations/DCS World - MOOSE - Tasking - SEAD.pptx differ