Advertisement
CluelessDev

Finite Staete Machine & State classes (Status)

Apr 17th, 2023
854
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.82 KB | None | 0 0
  1. --[[
  2.     State & Finite State Machine classes, Note that this a single file. Documentation coming soon?
  3. --]]
  4.  
  5. export type Entity = any
  6.  
  7. export type IFSM = {
  8.     InitialState     : IState,
  9.     EntitiesStateMap : {[Entity]:IState},
  10.     States           : {[string]: IState},
  11.     ActiveEntites    : {[Entity]: true},
  12.     PrintStateChange: boolean,
  13.  
  14.     --> Methods
  15.     RegisterEntity             : (self: IFSM, entity: Entity) -> nil,
  16.     UnRegisterEntity           : (self: IFSM, entity: Entity) -> nil,
  17.     RegisterAndTurnOnEntity    : (self: IFSM, entity: Entity, ...any) -> nil,
  18.     UnRegisterAndTurnOffEntity : (self: IFSM, entity: Entity, ...any) -> nil,
  19.     TurnOnEntity               : (self: IFSM, entity: Entity, ...any) -> nil,
  20.     TurnOffEntity              : (self: IFSM, entity: Entity, ...any) -> nil,
  21.     TurnOn                     : (self: IFSM, ...any) -> nil,
  22.     TurnOff                    : (self: IFSM, ...any) -> nil,
  23.     Update                     : (dt: number) -> nil,
  24.     ChangeState                : (self: IFSM, entity: Entity, newState: IState, ...any) -> nil
  25. }
  26.  
  27.  
  28. export type IState = {
  29.     Name: string,
  30.     Entities : {[Entity]: true},
  31.    
  32.     --> Callbacks
  33.     OnEnter  : (entity: Entity, fsm: IFSM, ...any) -> nil,
  34.     OnUpdate : (entity: Entity, fsm: IFSM, dt: number, ...any) -> nil,
  35.     OnExit   : (entity: Entity, fsm: IFSM, ...any) -> nil,
  36.  
  37.     --> Methods
  38.     Enter  : (self: IState, entity: Entity, fsm: IFSM, ...any) -> nil,
  39.     Exit   : (self: IState, entity: Entity, fsm: IFSM, ...any) -> nil,
  40.     Update : (self: IState, entity: Entity, fsm: IFSM, dt: number, ...any) -> nil,
  41. }
  42.  
  43.  
  44.  
  45. function GetSetIntersection(set1, set2)
  46.     local result = {}
  47.     for k in pairs(set1) do
  48.       if set2[k] then
  49.         result[k] = true
  50.       end
  51.     end
  52.     return result
  53.   end
  54.  
  55.  
  56. function GetSetDifference(set1, set2)
  57.     local result = {}
  58.     for element in pairs(set1) do
  59.         if not set2[element] then
  60.             result[element] = true
  61.         end
  62.     end
  63.     return result
  64. end
  65.  
  66.  
  67. -- !== ================================================================================||>
  68. -- !==                                      FSM
  69. -- !== ================================================================================||>
  70.  
  71. local finiteStateMachine = {}
  72. finiteStateMachine.__index = finiteStateMachine
  73.  
  74. function finiteStateMachine.new(initialState: IState, statesList: {[string]: IState}): IFSM
  75.     local self = setmetatable({}, finiteStateMachine) :: IFSM
  76.     self.InitialState     = initialState
  77.     self.States           = statesList
  78.     self.EntitiesStateMap = {}
  79.     self.ActiveEntites    = {}
  80.  
  81.     self.PrintStateChange = true
  82.  
  83.  
  84.     return self
  85. end
  86.  
  87.  
  88.  
  89. function finiteStateMachine:RegisterEntity(entity: Entity)
  90.     self.EntitiesStateMap[entity] = self.InitialState
  91. end
  92.  
  93. function finiteStateMachine:UnRegisterEntity(entity: Entity)
  94.     self.ActiveEntites[entity]    = nil
  95.     self.EntitiesStateMap[entity] = nil
  96. end
  97.  
  98.  
  99. function finiteStateMachine:TurnOnEntity(entity, ...)
  100.     if not self.EntitiesStateMap[entity] then
  101.         warn(entity, "is not registered in the state machine!")
  102.         return
  103.     end
  104.    
  105.     self.EntitiesStateMap[entity]:Enter(entity, self, ...)
  106.     self.ActiveEntites[entity] = true
  107. end
  108.  
  109. function finiteStateMachine:TurnOffEntity(entity, ...)
  110.     if self.EntitiesStateMap[entity] and self.ActiveEntites[entity] then
  111.         self.EntitiesStateMap[entity]:Exit(entity, self, ...)
  112.         self.ActiveEntites[entity] = false
  113.     elseif self.EntitiesStateMap[entity] and not self.ActiveEntites[entity] then
  114.         warn(entity, "is not active")
  115.     else
  116.         warn(entity, "is not registered in the machine")
  117.     end
  118. end
  119.  
  120.  
  121.  
  122.  
  123. function finiteStateMachine:RegisterAndTurnOnEntity(entity: Entity, ...)
  124.     if self.EntitiesStateMap[entity] and self.ActiveEntites[entity] then
  125.         warn(entity, "is already registered & active!")
  126.         return
  127.     end
  128.     self.EntitiesStateMap[entity] = self.InitialState
  129.     self:TurnOnEntity(entity, ...)
  130. end
  131.  
  132.  
  133.  
  134. function finiteStateMachine:UnRegisterAndTurnOffEntity(entity: Entity, ...)
  135.     if not self.EntitiesStateMap[entity] then
  136.         warn(entity, "is not registered in the machine!")
  137.         return
  138.     end
  139.     self.EntitiesStateMap[entity]:Exit(entity, self, ...)
  140.     self:UnRegisterEntity(entity)
  141. end
  142.  
  143.  
  144.  
  145. function finiteStateMachine:TurnOn(...)
  146.     local inactiveRegisteredInstances = GetSetDifference(self.EntitiesStateMap, self.ActiveEntites)
  147.     for entity in inactiveRegisteredInstances do
  148.         self:TurnOnEntity(entity, ...)
  149.     end
  150. end
  151.  
  152.  
  153. function finiteStateMachine:TurnOff(...)
  154.     local activeRegisteredEntities = GetSetIntersection(self.EntitiesStateMap, self.ActiveEntites)
  155.     for entity in activeRegisteredEntities do
  156.         self.EntitiesStateMap[entity]:Exit(entity, self, ...)
  157.         self.ActiveEntites[entity] = nil
  158.     end
  159. end
  160.  
  161.  
  162.  
  163.  
  164. function finiteStateMachine:ChangeState(entity: Entity, newState: IState, ...)
  165.     local currentState = self.EntitiesStateMap[entity]
  166.     if currentState then
  167.         if self.PrintStateChange then
  168.             warn(entity, "Coming from:", currentState.Name, "To:", newState.Name)
  169.         end
  170.  
  171.         self.EntitiesStateMap[entity]:Exit(entity, self, ...)
  172.         self.EntitiesStateMap[entity] = newState
  173.         self.EntitiesStateMap[entity]:Enter(entity, self, ...)
  174.  
  175.  
  176.  
  177.     else
  178.         warn(entity, "is not registered in the machine")
  179.     end
  180. end
  181.  
  182.  
  183. function finiteStateMachine:Update(dt)
  184.     local activeRegisteredEntities = GetSetIntersection(self.EntitiesStateMap, self.ActiveEntites)
  185.     for entity in activeRegisteredEntities do
  186.         self.EntitiesStateMap[entity]:Update(entity, self, dt)
  187.     end
  188. end
  189.  
  190.  
  191.  
  192.  
  193.  
  194. -- !== ================================================================================||>
  195. -- !==                                      State
  196. -- !== ================================================================================||>
  197.  
  198. local state = {}
  199. state.__index = state
  200.  
  201. function state.new(args)
  202.     local self = setmetatable({}, state)
  203.     self.Entities = {}
  204.     self.Name     = args.Name
  205.     self.OnEnter  = args.OnEnter or function()end
  206.     self.OnUpdate = args.OnUpdate or function()end
  207.     self.OnExit   = args.OnExit or function()end
  208.  
  209.     return self
  210. end
  211.  
  212.  
  213. function state:Enter(subject, fsm, ...)
  214.     self.Entities[subject] = true
  215.     self.OnEnter(subject, fsm, ...)
  216. end
  217.  
  218. function state:Update(subject, fsm, dt, ...)
  219.     if self.Entities[subject] then
  220.         self.OnUpdate(subject, fsm, dt, ...)
  221.     else
  222.         warn( if self.Name then "Entity is not in ".. self.Name else "Entity is not in state")
  223.     end
  224. end
  225.    
  226. function state:Exit(subject, fsm, ...)
  227.     self.Entities[subject] = nil
  228.     self.OnExit(subject, fsm, ...)
  229. end
  230.  
  231.  
  232.  
  233. return {
  234.     FSM = finiteStateMachine,
  235.     State = state,
  236. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement