local Tactree = {} local Nodes = {} local function NodeReset(node) node:_finish() node._started = false end local function NodeStart(node, args) -- don't start again if started if node._started then return end node.data = node.parent and node.parent.data or args or node.data node.data[node] = node.data[node] or {} node:_start() node._started = true end local function NodeUpdate(node, ...) -- automatically start if not started if not node._started then NodeStart(node) end -- mark node as run and unmark children node._ran = true for _, child in ipairs(node.children) do child._ran = false end local result = node:_tick(...) if result ~= nil then NodeReset(node) end -- reset unrun children for _, child in ipairs(node.children) do if not child._ran and child._started then NodeReset(child) end end return result end local function NodePrivateData(node) return node.data[node] end local function default_impl(node) end function Tactree.Leaf(name) return function(t) if Nodes[name] then error('node with name \'' .. name .. '\' already exists.', 2) else Nodes[name] = {} end local node_type = Nodes[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.Composite(name) return function(parent_type_name) return function(children) if Nodes[name] then error('node with name \'' .. name .. '\' already exists.', 2) else if not Nodes[parent_type_name] then error('no such node \'' .. parent_type_name .. '\'', 2) end Nodes[name] = setmetatable({}, { __index = Nodes[parent_type_name] }) end local node_type = Nodes[name] node_type.children = {} for _, child in ipairs(children) do if type(child) ~= 'string' then error('invalid child node', 2) end table.insert(node_type.children, child) end end end end local function tree_by_name(name, depth) depth = depth or 2 if not Nodes[name] then error('no such node \'' .. name .. '\'', depth) end local node = {} node.children = {} node.start = NodeStart node.update = NodeUpdate node.private = NodePrivateData -- this will crash if there is a recursive structure; find a way to prevent this? -- does my API even allow declaring recursive structures? if Nodes[name].children then for _, child in ipairs(Nodes[name].children) do local cn = tree_by_name(child, depth + 1) cn.parent = node table.insert(node.children, cn) end end return setmetatable(node, { __index = Nodes[name], __tostring = function() return name end }) end function Tactree.Tree(name) if not Nodes[name] then error('no such node \'' .. name .. '\'', 2) end return function(children) local node = {} node.children = {} node.start = NodeStart node.update = NodeUpdate node.private = NodePrivateData -- add pre-defined children if Nodes[name].children then for _, child in ipairs(Nodes[name].children) do local cn = tree_by_name(child, 3) cn.parent = node table.insert(node.children, cn) end end -- add extra children from call for _, child in ipairs(children) do local cn if type(child) == 'string' then cn = tree_by_name(child, 3) elseif type(child) == 'table' then cn = child else error('invalid child type: \'' .. type(child) '\'', 3) end cn.parent = node table.insert(node.children, cn) end return setmetatable(node, { __index = Nodes[name], __tostring = function() return name end }) end end Tactree.Leaf 'Sequence' { function(node, ...) local child = 1 local result repeat result = node.children[child]:update(...) child = child + 1 until (not result) or (child > #node.children) return result end } Tactree.Leaf 'StepSequence' { start = function(node) node:private().current_child = 1 end, tick = function(node, ...) local result repeat result = node.children[node:private().current_child]:update(...) if result ~= nil then node:private().current_child = node:private().current_child + 1 end until (not result) or (node:private().current_child > #node.children) return result end } Tactree.Leaf 'Selector' { function(node, ...) local child = 1 local result repeat result = node.children[child]:update(...) child = child + 1 until (result ~= false) or (child > #node.children) return result end } Tactree.Leaf 'StepSelector' { start = function(node) node:private().current_child = 1 end, tick = function(node, ...) local result repeat result = node.children[node:private().current_child]:update(...) if result ~= nil then node:private().current_child = node:private().current_child + 1 end until (result ~= false) or (node:private().current_child > #node.children) return result end } Tactree.Leaf 'Inverter' { function(node, ...) local result = node.children[1]:update(...) if result ~= nil then return not result end end } return Tactree