5e0d58cfad
EN: 1.2.0.60 CN: 1.2.0.61 JP: 1.2.0.63 KR: 1.2.0.67
162 lines
4.2 KiB
Lua
162 lines
4.2 KiB
Lua
local unpack = unpack or table.unpack
|
|
local M = {}
|
|
M.WILDCARD = "*"
|
|
M.DEFERRED = 0
|
|
M.SUCCEEDED = 1
|
|
M.NO_TRANSITION = 2
|
|
M.PENDING = 3
|
|
M.CANCELLED = 4
|
|
local do_callback = function(handler, args)
|
|
if handler then
|
|
return handler(unpack(args))
|
|
end
|
|
end
|
|
local before_event = function(self, event, _from, _to, args)
|
|
local specific = do_callback(self["on_before_" .. event], args)
|
|
local general = do_callback(self.on_before_event, args)
|
|
if specific == false or general == false then
|
|
return false
|
|
end
|
|
end
|
|
local leave_state = function(self, _event, from, _to, args)
|
|
local specific = do_callback(self["on_leave_" .. from], args)
|
|
local general = do_callback(self.on_leave_state, args)
|
|
if specific == false or general == false then
|
|
return false
|
|
end
|
|
if specific == M.DEFERRED or general == M.DEFERRED then
|
|
return M.DEFERRED
|
|
end
|
|
end
|
|
local enter_state = function(self, _event, _from, to, args)
|
|
do_callback(self["on_enter_" .. to] or self["on_" .. to], args)
|
|
do_callback(self.on_enter_state or self.on_state, args)
|
|
end
|
|
local after_event = function(self, event, _from, _to, args)
|
|
do_callback(self["on_after_" .. event] or self["on_" .. event], args)
|
|
do_callback(self.on_after_event or self.on_event, args)
|
|
end
|
|
local build_transition = function(self, event, states)
|
|
return function(...)
|
|
local from = self.current
|
|
local to = states[from] or states[M.WILDCARD] or from
|
|
local args = {
|
|
self,
|
|
event,
|
|
from,
|
|
to,
|
|
...
|
|
}
|
|
assert(not self.is_pending(), "previous transition still pending")
|
|
assert(self.can(event), "invalid transition from state '" .. from .. "' with event '" .. event .. "'")
|
|
local before = before_event(self, event, from, to, args)
|
|
if before == false then
|
|
return M.CANCELLED
|
|
end
|
|
if from == to then
|
|
after_event(self, event, from, to, args)
|
|
return M.NO_TRANSITION
|
|
end
|
|
function self.confirm()
|
|
self.confirm = nil
|
|
self.cancel = nil
|
|
self.current = to
|
|
enter_state(self, event, from, to, args)
|
|
after_event(self, event, from, to, args)
|
|
return M.SUCCEEDED
|
|
end
|
|
function self.cancel()
|
|
self.confirm = nil
|
|
self.cancel = nil
|
|
after_event(self, event, from, to, args)
|
|
return M.CANCELLED
|
|
end
|
|
local leave = leave_state(self, event, from, to, args)
|
|
if leave == false then
|
|
return M.CANCELLED
|
|
end
|
|
if leave == M.DEFERRED then
|
|
return M.PENDING
|
|
end
|
|
if self.confirm then
|
|
return self.confirm()
|
|
end
|
|
end
|
|
end
|
|
function M.create(cfg, target)
|
|
local self = target or {}
|
|
local initial = cfg.initial
|
|
initial = type(initial) == "string" and {state = initial} or initial
|
|
local initial_event = initial and initial.event or "startup"
|
|
local terminal = cfg.terminal
|
|
local events = cfg.events or {}
|
|
local callbacks = cfg.callbacks or {}
|
|
local states_for_event = {}
|
|
local events_for_state = {}
|
|
local add = function(e)
|
|
local from = type(e.from) == "table" and e.from or e.from and {
|
|
e.from
|
|
} or {
|
|
M.WILDCARD
|
|
}
|
|
local to = e.to
|
|
local event = e.name
|
|
states_for_event[event] = states_for_event[event] or {}
|
|
for _, fr in ipairs(from) do
|
|
events_for_state[fr] = events_for_state[fr] or {}
|
|
table.insert(events_for_state[fr], event)
|
|
states_for_event[event][fr] = to or fr
|
|
end
|
|
end
|
|
if initial then
|
|
add({
|
|
name = initial_event,
|
|
from = "none",
|
|
to = initial.state
|
|
})
|
|
end
|
|
for _, event in ipairs(events) do
|
|
add(event)
|
|
end
|
|
for event, states in pairs(states_for_event) do
|
|
self[event] = build_transition(self, event, states)
|
|
end
|
|
for name, callback in pairs(callbacks) do
|
|
self[name] = callback
|
|
end
|
|
self.current = "none"
|
|
function self.is(state)
|
|
if type(state) == "table" then
|
|
for _, s in ipairs(state) do
|
|
if self.current == s then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
return self.current == state
|
|
end
|
|
function self.can(event)
|
|
local states = states_for_event[event]
|
|
local to = states[self.current] or states[M.WILDCARD]
|
|
return to ~= nil
|
|
end
|
|
function self.cannot(event)
|
|
return not self.can(event)
|
|
end
|
|
function self.transitions()
|
|
return events_for_state[self.current]
|
|
end
|
|
function self.is_pending()
|
|
return self.confirm ~= nil
|
|
end
|
|
function self.is_finished()
|
|
return self.is(terminal)
|
|
end
|
|
if initial and not initial.defer then
|
|
self[initial_event]()
|
|
end
|
|
return self
|
|
end
|
|
return M
|