aboutsummaryrefslogtreecommitdiff
path: root/src/libjin-lua/scripts/ai/state_machine.lua
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2018-12-22 13:45:32 +0800
committerchai <chaifix@163.com>2018-12-22 13:45:32 +0800
commitdf70a616f58aa51ff6375a824fc18cbbc369e43b (patch)
treeb519fdfb2ae07de3d8dd2d08afdb7efe65457d7b /src/libjin-lua/scripts/ai/state_machine.lua
parent8422546ca0524e9d1f96b858d0f914fe9e6e9bff (diff)
+statemachine
Diffstat (limited to 'src/libjin-lua/scripts/ai/state_machine.lua')
-rw-r--r--src/libjin-lua/scripts/ai/state_machine.lua182
1 files changed, 180 insertions, 2 deletions
diff --git a/src/libjin-lua/scripts/ai/state_machine.lua b/src/libjin-lua/scripts/ai/state_machine.lua
index b4ec768..296296f 100644
--- a/src/libjin-lua/scripts/ai/state_machine.lua
+++ b/src/libjin-lua/scripts/ai/state_machine.lua
@@ -1,7 +1,185 @@
jin.ai = jin.ai or {}
-local statemachine = {}
+--[[
-jin.ai.newStateMachine = function()
+https://github.com/kyleconroy/lua-state-machine
+Copyright (c) 2012 Kyle Conroy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+]]
+local machine = {}
+machine.__index = machine
+
+local NONE = "none"
+local ASYNC = "async"
+
+local function call_handler(handler, params)
+ if handler then
+ return handler(unpack(params))
+ end
+end
+
+local function create_transition(name)
+ local can, to, from, params
+
+ local function transition(self, ...)
+ if self.asyncState == NONE then
+ can, to = self:can(name)
+ from = self.current
+ params = { self, name, from, to, ...}
+
+ if not can then return false end
+ self.currentTransitioningEvent = name
+
+ local beforeReturn = call_handler(self["onbefore" .. name], params)
+ local leaveReturn = call_handler(self["onleave" .. from], params)
+
+ if beforeReturn == false or leaveReturn == false then
+ return false
+ end
+
+ self.asyncState = name .. "WaitingOnLeave"
+
+ if leaveReturn ~= ASYNC then
+ transition(self, ...)
+ end
+
+ return true
+ elseif self.asyncState == name .. "WaitingOnLeave" then
+ self.current = to
+
+ local enterReturn = call_handler(self["onenter" .. to] or self["on" .. to], params)
+
+ self.asyncState = name .. "WaitingOnEnter"
+
+ if enterReturn ~= ASYNC then
+ transition(self, ...)
+ end
+
+ return true
+ elseif self.asyncState == name .. "WaitingOnEnter" then
+ call_handler(self["onafter" .. name] or self["on" .. name], params)
+ call_handler(self["onstatechange"], params)
+ self.asyncState = NONE
+ self.currentTransitioningEvent = nil
+ return true
+ else
+ if string.find(self.asyncState, "WaitingOnLeave") or string.find(self.asyncState, "WaitingOnEnter") then
+ self.asyncState = NONE
+ transition(self, ...)
+ return true
+ end
+ end
+
+ self.currentTransitioningEvent = nil
+ return false
+ end
+
+ return transition
+end
+
+local function add_to_map(map, event)
+ if type(event.from) == 'string' then
+ map[event.from] = event.to
+ else
+ for _, from in ipairs(event.from) do
+ map[from] = event.to
+ end
+ end
end
+
+function machine.create(options)
+ assert(options.events)
+
+ local fsm = {}
+ setmetatable(fsm, machine)
+
+ fsm.options = options
+ fsm.current = options.initial or 'none'
+ fsm.asyncState = NONE
+ fsm.events = {}
+
+ for _, event in ipairs(options.events or {}) do
+ local name = event.name
+ fsm[name] = fsm[name] or create_transition(name)
+ fsm.events[name] = fsm.events[name] or { map = {} }
+ add_to_map(fsm.events[name].map, event)
+ end
+
+ for name, callback in pairs(options.callbacks or {}) do
+ fsm[name] = callback
+ end
+
+ return fsm
+end
+
+function machine:is(state)
+ return self.current == state
+end
+
+function machine:can(e)
+ local event = self.events[e]
+ local to = event and event.map[self.current] or event.map['*']
+ return to ~= nil, to
+end
+
+function machine:cannot(e)
+ return not self:can(e)
+end
+
+function machine:todot(filename)
+ local dotfile = io.open(filename,'w')
+ dotfile:write('digraph {\n')
+ local transition = function(event,from,to)
+ dotfile:write(string.format('%s -> %s [label=%s];\n',from,to,event))
+ end
+ for _, event in pairs(self.options.events) do
+ if type(event.from) == 'table' then
+ for _, from in ipairs(event.from) do
+ transition(event.name,from,event.to)
+ end
+ else
+ transition(event.name,event.from,event.to)
+ end
+ end
+ dotfile:write('}\n')
+ dotfile:close()
+end
+
+function machine:transition(event)
+ if self.currentTransitioningEvent == event then
+ return self[self.currentTransitioningEvent](self)
+ end
+end
+
+function machine:cancelTransition(event)
+ if self.currentTransitioningEvent == event then
+ self.asyncState = NONE
+ self.currentTransitioningEvent = nil
+ end
+end
+
+machine.NONE = NONE
+machine.ASYNC = ASYNC
+
+-- Import to Jin.
+
+jin.ai.newStateMachine = machine.create