Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/StateMachine/Classes/State.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,21 @@ State.Name = "" :: string
@prop Transitions string
@within State

A reference for the transitions of this state. This is usually set while creating the state
A reference for the transitions of this state. This is usually set while creating the state.
Transitions can be defined with an optional priority. If nothing is defined then the default priority
of 0 is used. In this case GoToRed is evaluated first before GoToBlue.

```lua
local GoToBlue = require(script.Parent.Parent.Transitions.GoToBlue)
local GoToRed = require(script.Parent.Parent.Transitions.GoToRed)

local State = StateMachine.State

local Default = State.new("Default")
Default.Transitions = {GoToBlue}
Default.Transitions = {
{GoToBlue},
{GoToRed, 1}
}
```
]=]
State.Transitions = {} :: {Transition.Transition}
Expand Down
79 changes: 76 additions & 3 deletions src/StateMachine/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ local DUPLICATE_ERROR: string = "There cannot be more than 1 state by the same n
local DATA_WARNING: string = "[Warning]: The data of this state machine is not a table. It will be converted to a table. Please do not set data to a non table object"
local STATE_NOT_FOUND: string = "Attempt to %s, but there is no state by the name of %s"
local WRONG_TRANSITION: string = "Attempt to add a transition that is not a transition"
local PRIORITY_DEFAULT = 0

-- Used for quicker access to the directories
local cacheDirectories = {} :: {[Instance]: {any}}
Expand Down Expand Up @@ -213,7 +214,20 @@ function StateMachine.new(initialState: string, states: {State}, initialData: {[

stateClone._transitions = {}

for _, transition: Transition in stateClone.Transitions do
for _, transitionInfo in stateClone.Transitions do
local transition, priority
if type(transitionInfo) == "table" and transitionInfo.Type == Transition.Type then
transition = transitionInfo
priority = nil -- Default priority
elseif type(transitionInfo) == "table" and #transitionInfo == 2 and
type(transitionInfo[1]) == "table" and transitionInfo[1].Type == Transition.Type and
type(transitionInfo[2]) == "number" then
transition = transitionInfo[1]
priority = transitionInfo[2]
else
error(WRONG_TRANSITION, 2)
end

if #transition.Name == 0 then
transition.Name = HttpService:GenerateGUID(false)
end
Expand All @@ -233,6 +247,7 @@ function StateMachine.new(initialState: string, states: {State}, initialData: {[
error(WRONG_TRANSITION, 2)
end

transitionClone.Priority = priority
transitionClone.Data = stateClone.Data
transitionClone._changeState = function(newState: string)
self:ChangeState(newState)
Expand All @@ -241,8 +256,10 @@ function StateMachine.new(initialState: string, states: {State}, initialData: {[
stateClone._transitions[transitionClone.Name] = transitionClone
task.spawn(transitionClone.OnInit, transitionClone, self.Data)
self._trove:Add(transitionClone, "OnDestroy")
end

end

stateClone._executionOrder = self:_BuildTransitionsExecutionOrder(stateClone.Transitions)

self._States[state.Name] = stateClone
task.spawn(stateClone.OnInit, stateClone, self.Data)
self._trove:Add(stateClone, "OnDestroy")
Expand Down Expand Up @@ -563,6 +580,62 @@ function StateMachine:_CheckTransitions(): ()
end
end

--[=[
Sorts Transitions by Priority and build an execution order
If no priority is set then it is considered to be of PRIORITY_DEFAULT priority
If a state has two or more transitions with the same priority then they will be
executed in the order they were added

@private

@param transitionsList {Transition}

@return executionOrder {}
]=]
function StateMachine:_BuildTransitionsExecutionOrder(transitionsList: {}): {}
if not transitionsList then return {} end

local prioritized = {}
local defaults = {}

for _, item in ipairs(transitionsList) do
local transition, priority
if type(item) == "table" and item.Type == Transition.Type then
transition = item
priority = item.Priority or PRIORITY_DEFAULT
elseif type(item) == "table" and #item == 2 then
transition = item[1]
priority = item[2] or PRIORITY_DEFAULT
else
error("Invalid transition format")
end

if priority ~= PRIORITY_DEFAULT then
prioritized[#prioritized + 1] = {
transition = transition,
priority = priority
}
else
defaults[#defaults + 1] = transition
end
end

table.sort(prioritized, function(a, b)
return a.priority > b.priority
end)

local executionOrder = {}

for _, item in ipairs(prioritized) do
executionOrder[#executionOrder + 1] = item.transition
end
for _, transition in ipairs(defaults) do
executionOrder[#executionOrder + 1] = transition
end

return executionOrder
end

--[=[
Calls the transition method of the given state

Expand Down