Behave/behave.moon
Paul Liverman III 6fe93b13ef added Class fn
2017-12-09 08:54:54 -08:00

328 lines
6.2 KiB
Plaintext

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
@started = false
run: =>
return success
update: (...) =>
result = success
if not @started and @start
result = @\start ...
@started = true
if result == success
result = @\run ...
if result == success
result = @\finish(...) if @finish
@started = false
return result
-- Runs children in order until one returns fail, or all succeed.
class Sequence extends Node
new: (@nodes={}) =>
super!
@index = 0
@_running = false
update: (...) =>
result = success
if @_running
result = @_running\update ...
unless result == running
@_running = false
while result == success and @index < #@nodes
@index += 1
result = @nodes[@index]\update ...
if result == running
@_running = @nodes[@index]
else
@index = 0
return result
-- Runs children in order until one succeeds or all fail.
class Selector extends Node
new: (@nodes={}) =>
super!
@index = 0
@_running = false
update: (...) =>
result = fail
if @_running
result = @_running\update ...
unless result == running
@_running = false
while result == fail and @index < #@nodes
@index += 1
result = @nodes[@index]\update ...
if result == running
@_running = @nodes[@index]
else
@index = 0
return result
-- Runs a random child.
class Random extends Node
new: (@nodes={}) =>
super!
update: (...) =>
index = math.floor math.random! * #@nodes + 1
return @nodes[index]\update ...
-- Randomizes order of nodes in between complete runs of them as a Sequence.
class RandomSequence extends Node
new: (@nodes={}) =>
super!
@_running = false
@shuffle!
shuffle: =>
@_shuffled = {}
for i = 1, #@nodes
r = math.random i
unless r == i
@_shuffled[i] = @_shuffled[r]
@_shuffled[r] = @nodes[i]
update: (...) =>
result = success
if @_running
result = @_running\update ...
unless result == running
@_running = false
local tmp
while result == success and #@_shuffled > 0
result = @_shuffled[1]\update ...
tmp = table.remove @_shuffled, 1
if result == running
@_running = tmp
else
@shuffle!
return result
-- Randomizes order of nodes in between complete runs of them as a Selector.
class RandomSelector extends Node
new: (@nodes={}) =>
super!
@_running = false
@shuffle!
shuffle: =>
@_shuffled = {}
for i = 1, #@nodes
r = math.random i
unless r == i
@_shuffled[i] = @_shuffled[r]
@_shuffled[r] = @nodes[i]
update: (...) =>
result = fail
if @_running
result = @_running\update ...
unless result == running
@_running = false
local tmp
while result == fail and #@_shuffled > 0
result = @_shuffled[1]\update ...
tmp = table.remove @_shuffled, 1
if result == running
@_running = tmp
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!
@counter = 1
@_running = false
update: (...) =>
result = success
if @_running
result = @node\update ...
unless result == running
@_running = false
while result == success and @counter < @cycles
@counter += 1
result = @node\update ...
if result == running
@_running = true
else
@counter = 1
return result
-- Returns success whether or not the node succeeds.
class Succeed extends Node
new: (@node=Node!) =>
super!
update: (...) =>
if running == @node\update ...
return running
else
return success
-- Returns fail whether or not the node fails.
class Fail extends Node
new: (@node=Node!) =>
super!
update: (...) =>
if running == @node\update ...
return running
else
return fail
-- Returns success when the node fails, and failure on success.
class Invert extends Node
new: (@node=Node!) =>
super!
update: (...) =>
result = @node\update ...
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 Node
new: (@node=Node!) =>
super!
@ran = false
update: (...) =>
unless @ran
result = @node\update ...
unless result == running
@run = 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 = setmetable {
__init: ->
__base: base
__name: name
}, {
__call: (cls, ...) ->
@ = setmetable({}, base)
cls.__init(@, ...)
return @
}
if parent
setmetable 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
behave = {
-- Leaf Node
:Node
-- Composite Nodes
:Sequence
:Selector
:Random
:RandomSequence
:RandomSelector
-- Decorator Nodes
:Repeat
:Succeed
:Fail
:Invert
:RunOnce
-- Return Values
:success
:running
:fail
-- Utility Fns
:Class
}
behave.clone = (object) ->
local new
cls = getmetatable(object).__class.__name
if cls == "Repeat"
new = behave[cls] object.cycles, object
else
new = behave[cls] object
if object.nodes
nodes = {}
for k,v in pairs object.nodes
nodes[k] = behave.clone v
new.nodes = nodes
elseif object.node
new.node = behave.clone object.node
return new
return behave