local Tactree = {} local Registry = {} local function NodeStart(node, args) -- abort if started if node.data then return end node.data = node.parent and node.parent.data or args node:_start() end local function NodeUpdate(node) -- abort if not started if not node.data then return end local result = node:_tick() if type(result) == 'boolean' then node:_finish() node.data = nil end return result end local function default_impl(node) end function Tactree.Node(name) return function(t) if Registry[name] then error('node with name \'' .. name .. '\' already exists.', 2) else Registry[name] = {} end local node_type = Registry[name] local tick = t.tick or t[1] if type(tick) ~= 'function' then error('no tick function supplied', 2) end node_type._tick = tick node_type._start = type(t.start) == 'function' and t.start or default_impl node_type._finish = type(t.finish) == 'function' and t.finish or default_impl end end function Tactree.Tree(name) if not Registry[name] then error('no such node \'' .. name .. '\'', 2) end local node = setmetatable({}, { __index = Registry[name] }) node.children = {} node.start = NodeStart node.update = NodeUpdate return function(children) if type(children) == 'table' then for _, child in ipairs(children) do if type(child) == 'string' then child = Tactree.Tree(child) elseif type(child) ~= 'table' then error('invalid child type: ' .. type(child), 2) end child.parent = node table.insert(node.children, child) end end return node end end --[[ Tactree.Node 'Find nearest object' { function (node) ... end } Tactree.Node 'Pick up object' { function (node) ... end} Tactree.Node 'Move' { function (node) return node.data.creature:move(node.data.tx, node.data.ty, dt) end } local t = Tactree.Tree 'Sequence' { 'Find nearest object', 'Move', 'Pick up object' } t:start(...) t:update() ]]-- return Tactree