2017-12-08 04:54:54 +00:00
|
|
|
success = setmetatable({}, {__tostring: -> return "success"})
|
|
|
|
running = setmetatable({}, {__tostring: -> return "running"})
|
|
|
|
fail = setmetatable({}, {__tostring: -> return "fail"})
|
|
|
|
|
|
|
|
class Node
|
|
|
|
new: (fns={}) =>
|
|
|
|
for key, value in pairs fns
|
|
|
|
@[key] = value
|
|
|
|
|
|
|
|
@success = success
|
|
|
|
@running = running
|
|
|
|
@fail = fail
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
@u = {}
|
|
|
|
|
|
|
|
addObject: (obj) =>
|
|
|
|
obj[@u] = {
|
|
|
|
started: false
|
|
|
|
}
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
run: =>
|
|
|
|
return success
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
update: (obj, ...) =>
|
2017-12-08 04:54:54 +00:00
|
|
|
result = success
|
2017-12-09 17:31:35 +00:00
|
|
|
if not obj[@u].started and @start
|
|
|
|
result = @\start obj, ...
|
|
|
|
obj[@u].started = true
|
2017-12-08 04:54:54 +00:00
|
|
|
if result == success
|
2017-12-09 17:31:35 +00:00
|
|
|
result = @\run obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
if result == success
|
2017-12-09 17:31:35 +00:00
|
|
|
result = @\finish(obj, ...) if @finish
|
|
|
|
obj[@u].started = false
|
2017-12-08 04:54:54 +00:00
|
|
|
return result
|
|
|
|
|
|
|
|
-- Runs children in order until one returns fail, or all succeed.
|
|
|
|
class Sequence extends Node
|
|
|
|
new: (@nodes={}) =>
|
|
|
|
super!
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
addObject: (obj) =>
|
|
|
|
obj[@u] = {
|
|
|
|
index: 0
|
|
|
|
running: 0
|
|
|
|
}
|
2017-12-08 04:54:54 +00:00
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
update: (obj, ...) =>
|
2017-12-08 04:54:54 +00:00
|
|
|
result = success
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
if obj[@u].running
|
|
|
|
result = obj[@u].running\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
unless result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].running = false
|
2017-12-08 04:54:54 +00:00
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
while result == success and obj[@u].index < #@nodes
|
|
|
|
obj[@u].index += 1
|
|
|
|
result = @nodes[obj[@u].index]\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
if result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].running = @nodes[obj[@u].index]
|
2017-12-08 04:54:54 +00:00
|
|
|
else
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].index = 0
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
-- Runs children in order until one succeeds or all fail.
|
|
|
|
class Selector extends Node
|
|
|
|
new: (@nodes={}) =>
|
|
|
|
super!
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
addObject: (obj) =>
|
|
|
|
obj[@u] = {
|
|
|
|
index: 0
|
|
|
|
running: 0
|
|
|
|
}
|
2017-12-08 04:54:54 +00:00
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
update: (obj, ...) =>
|
2017-12-08 04:54:54 +00:00
|
|
|
result = fail
|
2017-12-09 17:31:35 +00:00
|
|
|
if obj[@u].running
|
|
|
|
result = obj[@u].running\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
unless result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].running = false
|
2017-12-08 04:54:54 +00:00
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
while result == fail and obj[@u].index < #@nodes
|
|
|
|
obj[@u].index += 1
|
|
|
|
result = @nodes[obj[@u].index]\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
if result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].running = @nodes[obj[@u].index]
|
2017-12-08 04:54:54 +00:00
|
|
|
else
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].index = 0
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
-- Runs a random child.
|
|
|
|
class Random extends Node
|
|
|
|
new: (@nodes={}) =>
|
|
|
|
super!
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
update: (obj, ...) =>
|
2017-12-08 04:54:54 +00:00
|
|
|
index = math.floor math.random! * #@nodes + 1
|
2017-12-09 17:31:35 +00:00
|
|
|
return @nodes[index]\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
class Randomizer extends Node
|
2017-12-08 04:54:54 +00:00
|
|
|
new: (@nodes={}) =>
|
|
|
|
super!
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
addObject: (obj) =>
|
|
|
|
obj[@u] = {
|
|
|
|
running: false
|
|
|
|
}
|
|
|
|
@shuffle obj
|
2017-12-08 04:54:54 +00:00
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
shuffle: (obj) =>
|
|
|
|
obj[@u].shuffledNodes = {}
|
2017-12-08 04:54:54 +00:00
|
|
|
for i = 1, #@nodes
|
|
|
|
r = math.random i
|
|
|
|
unless r == i
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].shuffledNodes[i] = obj[@u].shuffledNodes[r]
|
|
|
|
obj[@u].shuffledNodes[r] = @nodes[i]
|
2017-12-08 04:54:54 +00:00
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
-- Randomizes order of nodes in between complete runs of them as a Sequence.
|
|
|
|
class RandomSequence extends Randomizer
|
|
|
|
update: (obj, ...) =>
|
2017-12-08 04:54:54 +00:00
|
|
|
result = success
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
if obj[@u].running
|
|
|
|
result = obj[@u].running\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
unless result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].running = false
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
local tmp
|
2017-12-09 17:31:35 +00:00
|
|
|
while result == success and #obj[@u].shuffledNodes > 0
|
|
|
|
result = obj[@u].shuffledNodes[1]\update obj, ...
|
|
|
|
tmp = table.remove obj[@u].shuffledNodes, 1
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
if result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].running = tmp
|
2017-12-08 04:54:54 +00:00
|
|
|
else
|
2017-12-09 17:31:35 +00:00
|
|
|
@shuffle obj
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
-- Randomizes order of nodes in between complete runs of them as a Selector.
|
2017-12-09 17:31:35 +00:00
|
|
|
class RandomSelector extends Randomizer
|
|
|
|
update: (obj, ...) =>
|
2017-12-08 04:54:54 +00:00
|
|
|
result = fail
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
if obj[@u].running
|
|
|
|
result = obj[@u].running\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
unless result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].running = false
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
local tmp
|
2017-12-09 17:31:35 +00:00
|
|
|
while result == fail and #obj[@u].shuffledNodes > 0
|
|
|
|
result = obj[@u].shuffledNodes[1]\update obj, ...
|
|
|
|
tmp = table.remove obj[@u].shuffledNodes, 1
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
if result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].running = tmp
|
2017-12-08 04:54:54 +00:00
|
|
|
else
|
|
|
|
@shuffle!
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
-- Repeats a node a specified number of times, unless it fails.
|
|
|
|
class Repeat extends Node
|
|
|
|
new: (@cycles=2, @node=Node!) =>
|
|
|
|
super!
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
addObject: (obj) =>
|
|
|
|
obj[@u] = {
|
|
|
|
counter: 1
|
|
|
|
running: false
|
|
|
|
}
|
2017-12-08 04:54:54 +00:00
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
update: (obj, ...) =>
|
2017-12-08 04:54:54 +00:00
|
|
|
result = success
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
if obj[@u].running
|
|
|
|
result = @node\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
unless result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].running = false
|
2017-12-08 04:54:54 +00:00
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
while result == success and obj[@u].counter < @cycles
|
|
|
|
obj[@u].counter += 1
|
|
|
|
result = @node\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
if result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].running = true
|
2017-12-08 04:54:54 +00:00
|
|
|
else
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].counter = 1
|
2017-12-08 04:54:54 +00:00
|
|
|
|
|
|
|
return result
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
class Decorator extends Node
|
2017-12-08 04:54:54 +00:00
|
|
|
new: (@node=Node!) =>
|
|
|
|
super!
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
update: (obj, ...) =>
|
|
|
|
return @node\update obj, ...
|
|
|
|
|
|
|
|
-- Returns success whether or not the node succeeds.
|
|
|
|
class Succeed extends Decorator
|
|
|
|
update: (obj, ...) =>
|
|
|
|
if running == @node\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
return running
|
|
|
|
else
|
|
|
|
return success
|
|
|
|
|
|
|
|
-- Returns fail whether or not the node fails.
|
2017-12-09 17:31:35 +00:00
|
|
|
class Fail extends Decorator
|
|
|
|
update: (obj, ...) =>
|
|
|
|
if running == @node\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
return running
|
|
|
|
else
|
|
|
|
return fail
|
|
|
|
|
|
|
|
-- Returns success when the node fails, and failure on success.
|
2017-12-09 17:31:35 +00:00
|
|
|
class Invert extends Decorator
|
|
|
|
update: (obj, ...) =>
|
|
|
|
result = @node\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
if result == running
|
|
|
|
return running
|
|
|
|
elseif result == success
|
|
|
|
return fail
|
|
|
|
else
|
|
|
|
return success
|
|
|
|
|
|
|
|
-- Only runs children once, and returns fail from then on.
|
2017-12-09 17:31:35 +00:00
|
|
|
class RunOnce extends Decorator
|
|
|
|
addObject: (obj) =>
|
|
|
|
obj[@u] = {
|
|
|
|
ran: false
|
|
|
|
}
|
2017-12-08 04:54:54 +00:00
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
update: (obj, ...) =>
|
|
|
|
unless obj[@u].ran
|
|
|
|
result = @node\update obj, ...
|
2017-12-08 04:54:54 +00:00
|
|
|
unless result == running
|
2017-12-09 17:31:35 +00:00
|
|
|
obj[@u].ran = true
|
2017-12-08 04:54:54 +00:00
|
|
|
return result
|
|
|
|
else
|
|
|
|
return fail
|
|
|
|
|
2017-12-09 16:54:54 +00:00
|
|
|
-- A MoonScript-compatible class implementation for creating your own classes.
|
|
|
|
Class = (name, parent) ->
|
|
|
|
local newClass, base
|
|
|
|
base = {
|
|
|
|
__index: base
|
|
|
|
__class: newClass
|
|
|
|
}
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
newClass = setmetatable {
|
2017-12-09 16:54:54 +00:00
|
|
|
__init: ->
|
|
|
|
__base: base
|
|
|
|
__name: name
|
|
|
|
}, {
|
|
|
|
__call: (cls, ...) ->
|
2017-12-09 17:31:35 +00:00
|
|
|
@ = setmetatable({}, base)
|
2017-12-09 16:54:54 +00:00
|
|
|
cls.__init(@, ...)
|
|
|
|
return @
|
|
|
|
}
|
|
|
|
|
|
|
|
if parent
|
2017-12-09 17:31:35 +00:00
|
|
|
setmetatable base, {
|
2017-12-09 16:54:54 +00:00
|
|
|
__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
|
|
|
|
|
2017-12-09 17:31:35 +00:00
|
|
|
setmetatable {
|
2017-12-08 04:54:54 +00:00
|
|
|
-- Leaf Node
|
|
|
|
:Node
|
|
|
|
|
|
|
|
-- Composite Nodes
|
|
|
|
:Sequence
|
|
|
|
:Selector
|
|
|
|
:Random
|
|
|
|
:RandomSequence
|
|
|
|
:RandomSelector
|
|
|
|
|
|
|
|
-- Decorator Nodes
|
2017-12-09 17:31:35 +00:00
|
|
|
:Decorator
|
2017-12-08 04:54:54 +00:00
|
|
|
:Repeat
|
|
|
|
:Succeed
|
|
|
|
:Fail
|
|
|
|
:Invert
|
|
|
|
:RunOnce
|
|
|
|
|
|
|
|
-- Return Values
|
|
|
|
:success
|
|
|
|
:running
|
|
|
|
:fail
|
2017-12-09 16:54:54 +00:00
|
|
|
|
|
|
|
-- Utility Fns
|
|
|
|
:Class
|
2017-12-09 17:31:35 +00:00
|
|
|
}, {
|
|
|
|
__call: (bt, ...) ->
|
|
|
|
bt.Sequence ...
|
2017-12-08 04:54:54 +00:00
|
|
|
}
|