mirror of
https://github.com/TangentFoxy/Behave.git
synced 2024-11-15 11:04:21 +00:00
replaced w better implementation
This commit is contained in:
parent
b34b6c8bc5
commit
6455230334
156
ReadMe.md
156
ReadMe.md
@ -1,105 +1,87 @@
|
|||||||
# Behave
|
# Behave
|
||||||
|
|
||||||
A simple implementation of behavior trees in MoonScript / Lua.
|
A simple implementation of behavior trees in MoonScript / Lua. Define behaviors
|
||||||
|
as functions or small tables, then call `behave.make` on them to get a function
|
||||||
|
you can call repeatedly to execute behaviors.
|
||||||
|
|
||||||
|
A node should return a truthy or falsy value to indicate success or failure, or
|
||||||
|
`behave.running` to indicate that the node needs to be called again to continue
|
||||||
|
running.
|
||||||
|
|
||||||
|
Examples are in MoonScript, but shouldn't be too difficult to understand even if
|
||||||
|
you are unfamiliar with its syntax.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
(Note: The top example and last example work. The others are from experimenting
|
|
||||||
with ideas about how to make it easier to write code interfacing with this
|
|
||||||
library.)
|
|
||||||
|
|
||||||
```
|
```
|
||||||
bt = require "behave"
|
TODO
|
||||||
|
|
||||||
tree = bt.Sequence({
|
|
||||||
bt.Node({
|
|
||||||
run = function(self, obj, ...)
|
|
||||||
-- do stuff
|
|
||||||
end,
|
|
||||||
finish = function(self, obj, ...)
|
|
||||||
-- finish up
|
|
||||||
end
|
|
||||||
}),
|
|
||||||
bt.Random({
|
|
||||||
bt.Node({
|
|
||||||
-- add some functions
|
|
||||||
}),
|
|
||||||
bt.Node({
|
|
||||||
-- even more functionality!
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
tree:addObject(obj)
|
|
||||||
tree:update(obj)
|
|
||||||
|
|
||||||
node1 = {
|
|
||||||
-- pretend this has run/finish functions or whatever
|
|
||||||
}
|
|
||||||
node2 = {} -- same
|
|
||||||
bt.Make ({
|
|
||||||
"Sequence",
|
|
||||||
node1, node2, {
|
|
||||||
"Random",
|
|
||||||
node3, node4 -- these are in 'Random'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
tree = bt.Factory({
|
|
||||||
"Sequence",
|
|
||||||
{
|
|
||||||
"Node",
|
|
||||||
{
|
|
||||||
run = function(self) end,
|
|
||||||
finish = function(self) end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
node1 = bt.Node()
|
|
||||||
function node1:run()
|
|
||||||
-- do stuff!
|
|
||||||
end
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## The Leaf Node
|
## Leaf Nodes
|
||||||
|
|
||||||
`Node` accepts a table of values to be set on it. It expects a `run` function,
|
The easiest node is just a function that will be passed all arguments sent to a
|
||||||
and optionally a `start` and a `finish` function, which will only be run at the
|
behavior tree it is part of. There is a slightly more complex leaf node for
|
||||||
beginning and end of a node being run (whereas `run` can be called multiple
|
maintaining an internal state, and optional `start`/`finish` functions only
|
||||||
times).
|
called at the beginning and end of processing.
|
||||||
|
|
||||||
To run a behavior tree, call `update` on itself, with optional arguments (which
|
```
|
||||||
will be passed to their children as they are called).
|
WalkPath = {
|
||||||
|
start: (state, entity) ->
|
||||||
|
state.path = findPath(entity, entity.target)
|
||||||
|
return state.path
|
||||||
|
run: (state, entity) ->
|
||||||
|
-- this will not run if start fails
|
||||||
|
state.path.step!
|
||||||
|
finish: (state, entity) ->
|
||||||
|
-- this will run only if run returns a truthy value besides behave.running
|
||||||
|
state.path = nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## The Decorator Node
|
||||||
|
|
||||||
|
A very basic extension to the result of a leaf node, allowing you to alter the
|
||||||
|
result it returns. Note: You cannot alter a `behave.running` return.
|
||||||
|
|
||||||
|
```
|
||||||
|
InvertSomeNode = {
|
||||||
|
decorator: (result) ->
|
||||||
|
return not result
|
||||||
|
SomeNode
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Composite Nodes
|
## Composite Nodes
|
||||||
|
|
||||||
Pass a table of nodes to these to set their contents. All composite nodes repeat
|
Selector skips any failures and returns on the first node that returns a truthy
|
||||||
after the entire tree has been processed.
|
value (or returns `false` if nothing succeeds). Sequence continues until there
|
||||||
|
is a failure or returns success. Random executes a random node underneath it.
|
||||||
|
|
||||||
- Composite: Super class of the other composite nodes.
|
```
|
||||||
- Sequence: Runs children until a failure or success of all.
|
Behaviors = {
|
||||||
- Selector: Runs children until a success or failure of all.
|
type: behave.Random
|
||||||
- Random: Runs a random child and returns its result.
|
WalkRandomly, LookAtPhone, LeaveArea
|
||||||
- RandomSequence: Randomizes the order of its children, then acts like a
|
}
|
||||||
Sequence.
|
```
|
||||||
- RandomSelector: Randomizes the order of its children, then acts like a
|
|
||||||
Selector.
|
|
||||||
|
|
||||||
## Decorator Nodes
|
## Custom Nodes
|
||||||
|
|
||||||
Pass a single node to these, except for `Repeat`, which needs a number followed
|
You can create custom nodes and easily use them. Rather than explaining how to,
|
||||||
by a node. All decorator nodes (except `RunOnce`) repeat after the entire tree
|
here's an example:
|
||||||
has been processed.
|
|
||||||
|
|
||||||
- Decorator: Superclass. Just returns the result of its child node.
|
```
|
||||||
- Repeat: Repeats a node a specified number of times, fails if the node fails.
|
-- defining the node type:
|
||||||
- Succeed: Runs a node and returns success.
|
Invert = (tab) -> -- your fn will be passed a table describing the node
|
||||||
- Fail: Runs a node and returns failure.
|
node = behave.make tab[1] -- call make on any sub-nodes that you will be using, save the result
|
||||||
- Invert: Runs a node, reporting a success on fail, and failure on success.
|
return (...) -> -- use variable arguments, so sub-nodes can
|
||||||
- RunOnce: Runs a node, reporting its results, and fail after that.
|
result = node(...) -- get what they need
|
||||||
|
unless result == behave.running -- running nodes should not be interrupted
|
||||||
|
result = not result -- and finally we invert the result before
|
||||||
|
return result -- returning it
|
||||||
|
|
||||||
## Return Values
|
-- using the node type:
|
||||||
|
SomeInvertedNode = {
|
||||||
Within the module, `success`, `running`, and `fail` are defined. They are also
|
type: Invert -- this is how the library knows what function to call
|
||||||
present on all nodes (so you can use `self.success` and such).
|
SomeNode -- this is the node that will be inverted
|
||||||
|
}
|
||||||
|
```
|
||||||
|
910
behave.lua
910
behave.lua
@ -1,845 +1,129 @@
|
|||||||
local success = setmetatable({ }, {
|
local make, Node, Decorate
|
||||||
__tostring = function()
|
|
||||||
return "success"
|
|
||||||
end
|
|
||||||
})
|
|
||||||
local running = setmetatable({ }, {
|
local running = setmetatable({ }, {
|
||||||
__tostring = function()
|
__tostring = function()
|
||||||
return "running"
|
return "running"
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
local fail = setmetatable({ }, {
|
local get_nodes
|
||||||
__tostring = function()
|
get_nodes = function(tab)
|
||||||
return "fail"
|
local nodes = { }
|
||||||
|
for _index_0 = 1, #tab do
|
||||||
|
local node = tab[_index_0]
|
||||||
|
table.insert(nodes, make(node))
|
||||||
end
|
end
|
||||||
})
|
return nodes
|
||||||
local Node
|
end
|
||||||
do
|
make = function(tab)
|
||||||
local _class_0
|
if "function" == type(tab) then
|
||||||
local _base_0 = {
|
return tab
|
||||||
addObject = function(self, obj)
|
elseif "function" == type(tab.type) then
|
||||||
obj[self.u] = {
|
return tab.type(tab)
|
||||||
|
elseif tab.decorate then
|
||||||
|
return Decorate(tab)
|
||||||
|
else
|
||||||
|
return Node(tab)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Node = function(tab)
|
||||||
|
local state, started = { }, false
|
||||||
|
return function(...)
|
||||||
|
local result
|
||||||
|
if not (started) then
|
||||||
|
if tab.start then
|
||||||
|
result = tab.start(state, ...)
|
||||||
|
end
|
||||||
|
if not (result == false) then
|
||||||
|
started = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not (result == false) then
|
||||||
|
result = tab.run(state, ...)
|
||||||
|
end
|
||||||
|
if result ~= running then
|
||||||
started = false
|
started = false
|
||||||
}
|
if result and tab.finish then
|
||||||
end,
|
result = tab.finish(state, ...)
|
||||||
run = function(self)
|
|
||||||
return success
|
|
||||||
end,
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
local result = success
|
|
||||||
if not obj[self.u].started and self.start then
|
|
||||||
result = self:start(obj, ...)
|
|
||||||
obj[self.u].started = true
|
|
||||||
end
|
end
|
||||||
if result == success then
|
|
||||||
result = self:run(obj, ...)
|
|
||||||
end
|
|
||||||
if result == success then
|
|
||||||
if self.finish then
|
|
||||||
result = self:finish(obj, ...)
|
|
||||||
end
|
|
||||||
obj[self.u].started = false
|
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, fns)
|
|
||||||
if fns == nil then
|
|
||||||
fns = { }
|
|
||||||
end
|
|
||||||
for key, value in pairs(fns) do
|
|
||||||
self[key] = value
|
|
||||||
end
|
|
||||||
self.success = success
|
|
||||||
self.running = running
|
|
||||||
self.fail = fail
|
|
||||||
self.u = { }
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Node"
|
|
||||||
}, {
|
|
||||||
__index = _base_0,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
Node = _class_0
|
|
||||||
end
|
|
||||||
local Composite
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Node
|
|
||||||
local _base_0 = {
|
|
||||||
addObject = function(self, obj)
|
|
||||||
local _list_0 = self.nodes
|
|
||||||
for _index_0 = 1, #_list_0 do
|
|
||||||
local node = _list_0[_index_0]
|
|
||||||
node:addObject(obj)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, nodes)
|
|
||||||
if nodes == nil then
|
|
||||||
nodes = { }
|
|
||||||
end
|
|
||||||
self.nodes = nodes
|
|
||||||
return _class_0.__parent.__init(self)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Composite",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
Composite = _class_0
|
|
||||||
end
|
|
||||||
local Sequence
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Composite
|
|
||||||
local _base_0 = {
|
|
||||||
addObject = function(self, obj)
|
|
||||||
_class_0.__parent.__base.addObject(self)
|
|
||||||
obj[self.u] = {
|
|
||||||
index = 0,
|
|
||||||
running = 0
|
|
||||||
}
|
|
||||||
end,
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
local result = success
|
|
||||||
if obj[self.u].running then
|
|
||||||
result = obj[self.u].running:update(obj, ...)
|
|
||||||
if not (result == running) then
|
|
||||||
obj[self.u].running = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
while result == success and obj[self.u].index < #self.nodes do
|
|
||||||
obj[self.u].index = obj[self.u].index + 1
|
|
||||||
result = self.nodes[obj[self.u].index]:update(obj, ...)
|
|
||||||
end
|
|
||||||
if result == running then
|
|
||||||
obj[self.u].running = self.nodes[obj[self.u].index]
|
|
||||||
else
|
|
||||||
obj[self.u].index = 0
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, ...)
|
|
||||||
return _class_0.__parent.__init(self, ...)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Sequence",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
Sequence = _class_0
|
|
||||||
end
|
|
||||||
local Selector
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Composite
|
|
||||||
local _base_0 = {
|
|
||||||
addObject = function(self, obj)
|
|
||||||
_class_0.__parent.__base.addObject(self)
|
|
||||||
obj[self.u] = {
|
|
||||||
index = 0,
|
|
||||||
running = 0
|
|
||||||
}
|
|
||||||
end,
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
local result = fail
|
|
||||||
if obj[self.u].running then
|
|
||||||
result = obj[self.u].running:update(obj, ...)
|
|
||||||
if not (result == running) then
|
|
||||||
obj[self.u].running = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
while result == fail and obj[self.u].index < #self.nodes do
|
|
||||||
obj[self.u].index = obj[self.u].index + 1
|
|
||||||
result = self.nodes[obj[self.u].index]:update(obj, ...)
|
|
||||||
end
|
|
||||||
if result == running then
|
|
||||||
obj[self.u].running = self.nodes[obj[self.u].index]
|
|
||||||
else
|
|
||||||
obj[self.u].index = 0
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, ...)
|
|
||||||
return _class_0.__parent.__init(self, ...)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Selector",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
Selector = _class_0
|
|
||||||
end
|
|
||||||
local Random
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Composite
|
|
||||||
local _base_0 = {
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
local index = math.floor(math.random() * #self.nodes + 1)
|
|
||||||
return self.nodes[index]:update(obj, ...)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, ...)
|
|
||||||
return _class_0.__parent.__init(self, ...)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Random",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
Random = _class_0
|
|
||||||
end
|
|
||||||
local Randomizer
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Composite
|
|
||||||
local _base_0 = {
|
|
||||||
addObject = function(self, obj)
|
|
||||||
_class_0.__parent.__base.addObject(self)
|
|
||||||
obj[self.u] = {
|
|
||||||
running = false
|
|
||||||
}
|
|
||||||
return self:shuffle(obj)
|
|
||||||
end,
|
|
||||||
shuffle = function(self, obj)
|
|
||||||
obj[self.u].shuffledNodes = { }
|
|
||||||
for i = 1, #self.nodes do
|
|
||||||
local r = math.random(i)
|
|
||||||
if not (r == i) then
|
|
||||||
obj[self.u].shuffledNodes[i] = obj[self.u].shuffledNodes[r]
|
|
||||||
end
|
|
||||||
obj[self.u].shuffledNodes[r] = self.nodes[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, ...)
|
|
||||||
return _class_0.__parent.__init(self, ...)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Randomizer",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
Randomizer = _class_0
|
|
||||||
end
|
|
||||||
local RandomSequence
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Randomizer
|
|
||||||
local _base_0 = {
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
local result = success
|
|
||||||
if obj[self.u].running then
|
|
||||||
result = obj[self.u].running:update(obj, ...)
|
|
||||||
if not (result == running) then
|
|
||||||
obj[self.u].running = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local tmp
|
|
||||||
while result == success and #obj[self.u].shuffledNodes > 0 do
|
|
||||||
result = obj[self.u].shuffledNodes[1]:update(obj, ...)
|
|
||||||
tmp = table.remove(obj[self.u].shuffledNodes, 1)
|
|
||||||
end
|
|
||||||
if result == running then
|
|
||||||
obj[self.u].running = tmp
|
|
||||||
else
|
|
||||||
self:shuffle(obj)
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, ...)
|
|
||||||
return _class_0.__parent.__init(self, ...)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "RandomSequence",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
RandomSequence = _class_0
|
|
||||||
end
|
|
||||||
local RandomSelector
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Randomizer
|
|
||||||
local _base_0 = {
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
local result = fail
|
|
||||||
if obj[self.u].running then
|
|
||||||
result = obj[self.u].running:update(obj, ...)
|
|
||||||
if not (result == running) then
|
|
||||||
obj[self.u].running = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local tmp
|
|
||||||
while result == fail and #obj[self.u].shuffledNodes > 0 do
|
|
||||||
result = obj[self.u].shuffledNodes[1]:update(obj, ...)
|
|
||||||
tmp = table.remove(obj[self.u].shuffledNodes, 1)
|
|
||||||
end
|
|
||||||
if result == running then
|
|
||||||
obj[self.u].running = tmp
|
|
||||||
else
|
|
||||||
self:shuffle()
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, ...)
|
|
||||||
return _class_0.__parent.__init(self, ...)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "RandomSelector",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
RandomSelector = _class_0
|
|
||||||
end
|
end
|
||||||
local Decorator
|
local Decorator
|
||||||
do
|
Decorator = function(tab)
|
||||||
local _class_0
|
local node = make(tab[1])
|
||||||
local _parent_0 = Node
|
return function(...)
|
||||||
local _base_0 = {
|
local result = node(object, ...)
|
||||||
addObject = function(self, obj)
|
|
||||||
return self.node:addObject(obj)
|
|
||||||
end,
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
return self.node:update(obj, ...)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, node)
|
|
||||||
if node == nil then
|
|
||||||
node = Node()
|
|
||||||
end
|
|
||||||
self.node = node
|
|
||||||
return _class_0.__parent.__init(self)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Decorator",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
Decorator = _class_0
|
|
||||||
end
|
|
||||||
local Repeat
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Decorator
|
|
||||||
local _base_0 = {
|
|
||||||
addObject = function(self, obj)
|
|
||||||
_class_0.__parent.__base.addObject(self)
|
|
||||||
obj[self.u] = {
|
|
||||||
counter = 1,
|
|
||||||
running = false
|
|
||||||
}
|
|
||||||
end,
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
local result = success
|
|
||||||
if obj[self.u].running then
|
|
||||||
result = self.node:update(obj, ...)
|
|
||||||
if not (result == running) then
|
if not (result == running) then
|
||||||
obj[self.u].running = false
|
result = tab.decorate(result, ...)
|
||||||
end
|
|
||||||
end
|
|
||||||
while result == success and obj[self.u].counter < self.cycles do
|
|
||||||
obj[self.u].counter = obj[self.u].counter + 1
|
|
||||||
result = self.node:update(obj, ...)
|
|
||||||
end
|
|
||||||
if result == running then
|
|
||||||
obj[self.u].running = true
|
|
||||||
else
|
|
||||||
obj[self.u].counter = 1
|
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, cycles, node)
|
|
||||||
if cycles == nil then
|
|
||||||
cycles = 2
|
|
||||||
end
|
end
|
||||||
if node == nil then
|
local Selector
|
||||||
node = Node()
|
Selector = function(tab)
|
||||||
|
local nodes = get_nodes(tab)
|
||||||
|
local length = #nodes
|
||||||
|
local i = 1
|
||||||
|
return function(...)
|
||||||
|
local result = nodes[i](...)
|
||||||
|
while not result do
|
||||||
|
i = i + 1
|
||||||
|
if i > length then
|
||||||
|
i = 1
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
self.cycles, self.node = cycles, node
|
result = nodes[i](...)
|
||||||
return _class_0.__parent.__init(self, self.node)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Repeat",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
end
|
||||||
else
|
if result ~= running then
|
||||||
return val
|
i = 1
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
Repeat = _class_0
|
|
||||||
end
|
|
||||||
local Succeed
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Decorator
|
|
||||||
local _base_0 = {
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
if running == self.node:update(obj, ...) then
|
|
||||||
return running
|
|
||||||
else
|
|
||||||
return success
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, ...)
|
|
||||||
return _class_0.__parent.__init(self, ...)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Succeed",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
Succeed = _class_0
|
|
||||||
end
|
|
||||||
local Fail
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Decorator
|
|
||||||
local _base_0 = {
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
if running == self.node:update(obj, ...) then
|
|
||||||
return running
|
|
||||||
else
|
|
||||||
return fail
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, ...)
|
|
||||||
return _class_0.__parent.__init(self, ...)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Fail",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
Fail = _class_0
|
|
||||||
end
|
|
||||||
local Invert
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Decorator
|
|
||||||
local _base_0 = {
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
local result = self.node:update(obj, ...)
|
|
||||||
if result == running then
|
|
||||||
return running
|
|
||||||
elseif result == success then
|
|
||||||
return fail
|
|
||||||
else
|
|
||||||
return success
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
_class_0 = setmetatable({
|
|
||||||
__init = function(self, ...)
|
|
||||||
return _class_0.__parent.__init(self, ...)
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "Invert",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
Invert = _class_0
|
|
||||||
end
|
|
||||||
local RunOnce
|
|
||||||
do
|
|
||||||
local _class_0
|
|
||||||
local _parent_0 = Decorator
|
|
||||||
local _base_0 = {
|
|
||||||
addObject = function(self, obj)
|
|
||||||
obj[self.u] = {
|
|
||||||
ran = false
|
|
||||||
}
|
|
||||||
end,
|
|
||||||
update = function(self, obj, ...)
|
|
||||||
if not (obj[self.u].ran) then
|
|
||||||
local result = self.node:update(obj, ...)
|
|
||||||
if not (result == running) then
|
|
||||||
obj[self.u].ran = true
|
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
else
|
|
||||||
return fail
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
}
|
local Sequence
|
||||||
_base_0.__index = _base_0
|
Sequence = function(tab)
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
local nodes = get_nodes(tab)
|
||||||
_class_0 = setmetatable({
|
local length = #nodes
|
||||||
__init = function(self, ...)
|
local i = 1
|
||||||
return _class_0.__parent.__init(self, ...)
|
return function(...)
|
||||||
end,
|
local result = nodes[i](...)
|
||||||
__base = _base_0,
|
while result and result ~= running do
|
||||||
__name = "RunOnce",
|
i = i + 1
|
||||||
__parent = _parent_0
|
if i > length then
|
||||||
}, {
|
i = 1
|
||||||
__index = function(cls, name)
|
return result
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil then
|
|
||||||
local parent = rawget(cls, "__parent")
|
|
||||||
if parent then
|
|
||||||
return parent[name]
|
|
||||||
end
|
end
|
||||||
else
|
result = nodes[i](...)
|
||||||
return val
|
|
||||||
end
|
end
|
||||||
end,
|
if not (result) then
|
||||||
__call = function(cls, ...)
|
i = 1
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
end
|
||||||
})
|
return result
|
||||||
_base_0.__class = _class_0
|
|
||||||
if _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
RunOnce = _class_0
|
|
||||||
end
|
|
||||||
local Class
|
|
||||||
Class = function(name, parent)
|
|
||||||
local newClass, base
|
|
||||||
base = {
|
|
||||||
__index = base,
|
|
||||||
__class = newClass
|
|
||||||
}
|
|
||||||
newClass = setmetatable({
|
|
||||||
__init = function() end,
|
|
||||||
__base = base,
|
|
||||||
__name = name
|
|
||||||
}, {
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local self = setmetatable({ }, base)
|
|
||||||
cls.__init(self, ...)
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
})
|
|
||||||
if parent then
|
|
||||||
setmetatable(base, {
|
|
||||||
__parent = parent.__base
|
|
||||||
})
|
|
||||||
newClass.__parent = parent
|
|
||||||
newClass.__index = function(cls, name)
|
|
||||||
local val = rawget(base, name)
|
|
||||||
if val == nil then
|
|
||||||
return parent[name]
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if parent.__inherited then
|
local Random
|
||||||
parent:__inherited(newClass)
|
Random = function(tab)
|
||||||
|
local nodes = get_nodes(tab)
|
||||||
|
local length = #nodes
|
||||||
|
local r
|
||||||
|
return function(...)
|
||||||
|
if not (r) then
|
||||||
|
r = 1 + math.random(length)
|
||||||
|
end
|
||||||
|
local result = nodes[r](...)
|
||||||
|
if not (result == running) then
|
||||||
|
r = nil
|
||||||
|
end
|
||||||
|
return result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return newClass, base
|
return {
|
||||||
end
|
success = true,
|
||||||
return setmetatable({
|
|
||||||
Node = Node,
|
|
||||||
Sequence = Sequence,
|
|
||||||
Selector = Selector,
|
|
||||||
Random = Random,
|
|
||||||
RandomSequence = RandomSequence,
|
|
||||||
RandomSelector = RandomSelector,
|
|
||||||
Decorator = Decorator,
|
|
||||||
Repeat = Repeat,
|
|
||||||
Succeed = Succeed,
|
|
||||||
Fail = Fail,
|
|
||||||
Invert = Invert,
|
|
||||||
RunOnce = RunOnce,
|
|
||||||
success = success,
|
|
||||||
running = running,
|
running = running,
|
||||||
fail = fail,
|
failure = false,
|
||||||
Class = Class
|
Node = Node,
|
||||||
}, {
|
Decorator = Decorator,
|
||||||
__call = function(bt, ...)
|
Selector = Selector,
|
||||||
return bt.Sequence(...)
|
Sequence = Sequence,
|
||||||
end
|
Random = Random
|
||||||
})
|
}
|
||||||
|
386
behave.moon
386
behave.moon
@ -1,316 +1,100 @@
|
|||||||
success = setmetatable({}, {__tostring: -> return "success"})
|
local make, Node, Decorate
|
||||||
running = setmetatable({}, {__tostring: -> return "running"})
|
|
||||||
fail = setmetatable({}, {__tostring: -> return "fail"})
|
|
||||||
|
|
||||||
class Node
|
running = setmetatable {}, __tostring: -> return "running"
|
||||||
new: (fns={}) =>
|
|
||||||
for key, value in pairs fns
|
|
||||||
@[key] = value
|
|
||||||
|
|
||||||
@success = success
|
get_nodes = (tab) ->
|
||||||
@running = running
|
nodes = {}
|
||||||
@fail = fail
|
for node in *tab
|
||||||
|
table.insert nodes, make node
|
||||||
|
return nodes
|
||||||
|
|
||||||
@u = {}
|
-- produces a callable function from a defined node or full behavior tree
|
||||||
|
make = (tab) ->
|
||||||
|
if "function" == type tab
|
||||||
|
return tab
|
||||||
|
elseif "function" == type tab.type
|
||||||
|
return tab.type tab
|
||||||
|
elseif tab.decorate
|
||||||
|
return Decorate tab
|
||||||
|
else
|
||||||
|
return Node tab
|
||||||
|
|
||||||
addObject: (obj) =>
|
-- complex leaf node
|
||||||
obj[@u] = {
|
-- state is preserved through calls, optional start/finish functions
|
||||||
started: false
|
Node = (tab) ->
|
||||||
}
|
state, started = {}, false
|
||||||
|
return (...) ->
|
||||||
run: =>
|
local result
|
||||||
return success
|
unless started
|
||||||
|
result = tab.start state, ... if tab.start
|
||||||
update: (obj, ...) =>
|
started = true unless result == false
|
||||||
result = success
|
result = tab.run state, ... unless result == false
|
||||||
if not obj[@u].started and @start
|
if result != running
|
||||||
result = @\start obj, ...
|
started = false
|
||||||
obj[@u].started = true
|
result = tab.finish state, ... if result and tab.finish
|
||||||
if result == success
|
|
||||||
result = @\run obj, ...
|
|
||||||
if result == success
|
|
||||||
result = @\finish(obj, ...) if @finish
|
|
||||||
obj[@u].started = false
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class Composite extends Node
|
-- modifies the result of a node before returning it
|
||||||
new: (@nodes={}) =>
|
Decorator = (tab) ->
|
||||||
super!
|
node = make tab[1]
|
||||||
|
return (...) ->
|
||||||
|
result = node object, ...
|
||||||
|
result = tab.decorate result, ... unless result == running
|
||||||
|
return result
|
||||||
|
|
||||||
addObject: (obj) =>
|
-- returns first success/running, or failure
|
||||||
for node in *@nodes
|
Selector = (tab) ->
|
||||||
node\addObject obj
|
nodes = get_nodes tab
|
||||||
|
length = #nodes
|
||||||
|
i = 1
|
||||||
|
return (...) ->
|
||||||
|
result = nodes[i](...)
|
||||||
|
while not result
|
||||||
|
i += 1
|
||||||
|
if i > length
|
||||||
|
i = 1
|
||||||
|
return false
|
||||||
|
result = nodes[i](...)
|
||||||
|
i = 1 if result != running
|
||||||
|
return result
|
||||||
|
|
||||||
-- Runs children in order until one returns fail, or all succeed.
|
-- returns first running/failure, or success
|
||||||
class Sequence extends Composite
|
Sequence = (tab) ->
|
||||||
addObject: (obj) =>
|
nodes = get_nodes tab
|
||||||
super!
|
length = #nodes
|
||||||
obj[@u] = {
|
i = 1
|
||||||
index: 0
|
return (...) ->
|
||||||
running: 0
|
result = nodes[i](...)
|
||||||
}
|
while result and result != running
|
||||||
|
i += 1
|
||||||
|
if i > length
|
||||||
|
i = 1
|
||||||
|
return result
|
||||||
|
result = nodes[i](...)
|
||||||
|
i = 1 unless result
|
||||||
|
return result
|
||||||
|
|
||||||
update: (obj, ...) =>
|
-- returns success/running/failure from random child
|
||||||
result = success
|
Random = (tab) ->
|
||||||
|
nodes = get_nodes tab
|
||||||
if obj[@u].running
|
length = #nodes
|
||||||
result = obj[@u].running\update obj, ...
|
local r
|
||||||
|
return (...) ->
|
||||||
|
unless r
|
||||||
|
r = 1 + math.random length
|
||||||
|
result = nodes[r](...)
|
||||||
unless result == running
|
unless result == running
|
||||||
obj[@u].running = false
|
r = nil
|
||||||
|
|
||||||
while result == success and obj[@u].index < #@nodes
|
|
||||||
obj[@u].index += 1
|
|
||||||
result = @nodes[obj[@u].index]\update obj, ...
|
|
||||||
|
|
||||||
if result == running
|
|
||||||
obj[@u].running = @nodes[obj[@u].index]
|
|
||||||
else
|
|
||||||
obj[@u].index = 0
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
-- Runs children in order until one succeeds or all fail.
|
{
|
||||||
class Selector extends Composite
|
success: true
|
||||||
addObject: (obj) =>
|
|
||||||
super!
|
|
||||||
obj[@u] = {
|
|
||||||
index: 0
|
|
||||||
running: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
update: (obj, ...) =>
|
|
||||||
result = fail
|
|
||||||
if obj[@u].running
|
|
||||||
result = obj[@u].running\update obj, ...
|
|
||||||
unless result == running
|
|
||||||
obj[@u].running = false
|
|
||||||
|
|
||||||
while result == fail and obj[@u].index < #@nodes
|
|
||||||
obj[@u].index += 1
|
|
||||||
result = @nodes[obj[@u].index]\update obj, ...
|
|
||||||
|
|
||||||
if result == running
|
|
||||||
obj[@u].running = @nodes[obj[@u].index]
|
|
||||||
else
|
|
||||||
obj[@u].index = 0
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
-- Runs a random child.
|
|
||||||
class Random extends Composite
|
|
||||||
update: (obj, ...) =>
|
|
||||||
index = math.floor math.random! * #@nodes + 1
|
|
||||||
return @nodes[index]\update obj, ...
|
|
||||||
|
|
||||||
class Randomizer extends Composite
|
|
||||||
addObject: (obj) =>
|
|
||||||
super!
|
|
||||||
obj[@u] = {
|
|
||||||
running: false
|
|
||||||
}
|
|
||||||
@shuffle obj
|
|
||||||
|
|
||||||
shuffle: (obj) =>
|
|
||||||
obj[@u].shuffledNodes = {}
|
|
||||||
for i = 1, #@nodes
|
|
||||||
r = math.random i
|
|
||||||
unless r == i
|
|
||||||
obj[@u].shuffledNodes[i] = obj[@u].shuffledNodes[r]
|
|
||||||
obj[@u].shuffledNodes[r] = @nodes[i]
|
|
||||||
|
|
||||||
-- Randomizes order of nodes in between complete runs of them as a Sequence.
|
|
||||||
class RandomSequence extends Randomizer
|
|
||||||
update: (obj, ...) =>
|
|
||||||
result = success
|
|
||||||
|
|
||||||
if obj[@u].running
|
|
||||||
result = obj[@u].running\update obj, ...
|
|
||||||
unless result == running
|
|
||||||
obj[@u].running = false
|
|
||||||
|
|
||||||
local tmp
|
|
||||||
while result == success and #obj[@u].shuffledNodes > 0
|
|
||||||
result = obj[@u].shuffledNodes[1]\update obj, ...
|
|
||||||
tmp = table.remove obj[@u].shuffledNodes, 1
|
|
||||||
|
|
||||||
if result == running
|
|
||||||
obj[@u].running = tmp
|
|
||||||
else
|
|
||||||
@shuffle obj
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
-- Randomizes order of nodes in between complete runs of them as a Selector.
|
|
||||||
class RandomSelector extends Randomizer
|
|
||||||
update: (obj, ...) =>
|
|
||||||
result = fail
|
|
||||||
|
|
||||||
if obj[@u].running
|
|
||||||
result = obj[@u].running\update obj, ...
|
|
||||||
unless result == running
|
|
||||||
obj[@u].running = false
|
|
||||||
|
|
||||||
local tmp
|
|
||||||
while result == fail and #obj[@u].shuffledNodes > 0
|
|
||||||
result = obj[@u].shuffledNodes[1]\update obj, ...
|
|
||||||
tmp = table.remove obj[@u].shuffledNodes, 1
|
|
||||||
|
|
||||||
if result == running
|
|
||||||
obj[@u].running = tmp
|
|
||||||
else
|
|
||||||
@shuffle!
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
class Decorator extends Node
|
|
||||||
new: (@node=Node!) =>
|
|
||||||
super!
|
|
||||||
|
|
||||||
addObject: (obj) =>
|
|
||||||
@node\addObject obj
|
|
||||||
|
|
||||||
update: (obj, ...) =>
|
|
||||||
return @node\update obj, ...
|
|
||||||
|
|
||||||
-- Repeats a node a specified number of times, unless it fails.
|
|
||||||
class Repeat extends Decorator
|
|
||||||
new: (@cycles=2, @node=Node!) =>
|
|
||||||
super @node
|
|
||||||
|
|
||||||
addObject: (obj) =>
|
|
||||||
super!
|
|
||||||
obj[@u] = {
|
|
||||||
counter: 1
|
|
||||||
running: false
|
|
||||||
}
|
|
||||||
|
|
||||||
update: (obj, ...) =>
|
|
||||||
result = success
|
|
||||||
|
|
||||||
if obj[@u].running
|
|
||||||
result = @node\update obj, ...
|
|
||||||
unless result == running
|
|
||||||
obj[@u].running = false
|
|
||||||
|
|
||||||
while result == success and obj[@u].counter < @cycles
|
|
||||||
obj[@u].counter += 1
|
|
||||||
result = @node\update obj, ...
|
|
||||||
|
|
||||||
if result == running
|
|
||||||
obj[@u].running = true
|
|
||||||
else
|
|
||||||
obj[@u].counter = 1
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
-- Returns success whether or not the node succeeds.
|
|
||||||
class Succeed extends Decorator
|
|
||||||
update: (obj, ...) =>
|
|
||||||
if running == @node\update obj, ...
|
|
||||||
return running
|
|
||||||
else
|
|
||||||
return success
|
|
||||||
|
|
||||||
-- Returns fail whether or not the node fails.
|
|
||||||
class Fail extends Decorator
|
|
||||||
update: (obj, ...) =>
|
|
||||||
if running == @node\update obj, ...
|
|
||||||
return running
|
|
||||||
else
|
|
||||||
return fail
|
|
||||||
|
|
||||||
-- Returns success when the node fails, and failure on success.
|
|
||||||
class Invert extends Decorator
|
|
||||||
update: (obj, ...) =>
|
|
||||||
result = @node\update obj, ...
|
|
||||||
if result == running
|
|
||||||
return running
|
|
||||||
elseif result == success
|
|
||||||
return fail
|
|
||||||
else
|
|
||||||
return success
|
|
||||||
|
|
||||||
-- Only runs children once, and returns fail from then on.
|
|
||||||
class RunOnce extends Decorator
|
|
||||||
addObject: (obj) =>
|
|
||||||
obj[@u] = {
|
|
||||||
ran: false
|
|
||||||
}
|
|
||||||
|
|
||||||
update: (obj, ...) =>
|
|
||||||
unless obj[@u].ran
|
|
||||||
result = @node\update obj, ...
|
|
||||||
unless result == running
|
|
||||||
obj[@u].ran = true
|
|
||||||
return result
|
|
||||||
else
|
|
||||||
return fail
|
|
||||||
|
|
||||||
-- A MoonScript-compatible class implementation for creating your own classes.
|
|
||||||
Class = (name, parent) ->
|
|
||||||
local newClass, base
|
|
||||||
base = {
|
|
||||||
__index: base
|
|
||||||
__class: newClass
|
|
||||||
}
|
|
||||||
|
|
||||||
newClass = setmetatable {
|
|
||||||
__init: ->
|
|
||||||
__base: base
|
|
||||||
__name: name
|
|
||||||
}, {
|
|
||||||
__call: (cls, ...) ->
|
|
||||||
@ = setmetatable({}, base)
|
|
||||||
cls.__init(@, ...)
|
|
||||||
return @
|
|
||||||
}
|
|
||||||
|
|
||||||
if parent
|
|
||||||
setmetatable base, {
|
|
||||||
__parent: parent.__base
|
|
||||||
}
|
|
||||||
|
|
||||||
newClass.__parent = parent
|
|
||||||
newClass.__index = (cls, name) ->
|
|
||||||
val = rawget(base, name)
|
|
||||||
if val == nil
|
|
||||||
return parent[name]
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
|
|
||||||
if parent.__inherited
|
|
||||||
parent\__inherited newClass
|
|
||||||
|
|
||||||
return newClass, base
|
|
||||||
|
|
||||||
setmetatable {
|
|
||||||
-- Leaf Node
|
|
||||||
:Node
|
|
||||||
|
|
||||||
-- Composite Nodes
|
|
||||||
:Composite
|
|
||||||
:Sequence
|
|
||||||
:Selector
|
|
||||||
:Random
|
|
||||||
:RandomSequence
|
|
||||||
:RandomSelector
|
|
||||||
|
|
||||||
-- Decorator Nodes
|
|
||||||
:Decorator
|
|
||||||
:Repeat
|
|
||||||
:Succeed
|
|
||||||
:Fail
|
|
||||||
:Invert
|
|
||||||
:RunOnce
|
|
||||||
|
|
||||||
-- Return Values
|
|
||||||
:success
|
|
||||||
:running
|
:running
|
||||||
:fail
|
failure: false
|
||||||
|
|
||||||
-- Utility Fns
|
:Node
|
||||||
:Class
|
:Decorator
|
||||||
}, {
|
:Selector
|
||||||
__call: (bt, ...) ->
|
:Sequence
|
||||||
bt.Sequence ...
|
:Random
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user