Behave/behave.moon

313 lines
6.4 KiB
Plaintext
Raw Normal View History

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
@u = {}
addObject: (obj) =>
obj[@u] = {
started: false
}
2017-12-08 04:54:54 +00:00
run: =>
return success
update: (obj, ...) =>
2017-12-08 04:54:54 +00:00
result = success
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
result = @\run obj, ...
2017-12-08 04:54:54 +00:00
if result == success
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!
addObject: (obj) =>
obj[@u] = {
index: 0
running: 0
}
2017-12-08 04:54:54 +00:00
update: (obj, ...) =>
2017-12-08 04:54:54 +00:00
result = success
if obj[@u].running
result = obj[@u].running\update obj, ...
2017-12-08 04:54:54 +00:00
unless result == running
obj[@u].running = false
2017-12-08 04:54:54 +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
obj[@u].running = @nodes[obj[@u].index]
2017-12-08 04:54:54 +00:00
else
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!
addObject: (obj) =>
obj[@u] = {
index: 0
running: 0
}
2017-12-08 04:54:54 +00:00
update: (obj, ...) =>
2017-12-08 04:54:54 +00:00
result = fail
if obj[@u].running
result = obj[@u].running\update obj, ...
2017-12-08 04:54:54 +00:00
unless result == running
obj[@u].running = false
2017-12-08 04:54:54 +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
obj[@u].running = @nodes[obj[@u].index]
2017-12-08 04:54:54 +00:00
else
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!
update: (obj, ...) =>
2017-12-08 04:54:54 +00:00
index = math.floor math.random! * #@nodes + 1
return @nodes[index]\update obj, ...
2017-12-08 04:54:54 +00:00
class Randomizer extends Node
2017-12-08 04:54:54 +00:00
new: (@nodes={}) =>
super!
addObject: (obj) =>
obj[@u] = {
running: false
}
@shuffle obj
2017-12-08 04:54:54 +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
obj[@u].shuffledNodes[i] = obj[@u].shuffledNodes[r]
obj[@u].shuffledNodes[r] = @nodes[i]
2017-12-08 04:54:54 +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
if obj[@u].running
result = obj[@u].running\update obj, ...
2017-12-08 04:54:54 +00:00
unless result == running
obj[@u].running = false
2017-12-08 04:54:54 +00:00
local tmp
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
obj[@u].running = tmp
2017-12-08 04:54:54 +00:00
else
@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.
class RandomSelector extends Randomizer
update: (obj, ...) =>
2017-12-08 04:54:54 +00:00
result = fail
if obj[@u].running
result = obj[@u].running\update obj, ...
2017-12-08 04:54:54 +00:00
unless result == running
obj[@u].running = false
2017-12-08 04:54:54 +00:00
local tmp
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
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!
addObject: (obj) =>
obj[@u] = {
counter: 1
running: false
}
2017-12-08 04:54:54 +00:00
update: (obj, ...) =>
2017-12-08 04:54:54 +00:00
result = success
if obj[@u].running
result = @node\update obj, ...
2017-12-08 04:54:54 +00:00
unless result == running
obj[@u].running = false
2017-12-08 04:54:54 +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
obj[@u].running = true
2017-12-08 04:54:54 +00:00
else
obj[@u].counter = 1
2017-12-08 04:54:54 +00:00
return result
class Decorator extends Node
2017-12-08 04:54:54 +00:00
new: (@node=Node!) =>
super!
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.
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.
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.
class RunOnce extends Decorator
addObject: (obj) =>
obj[@u] = {
ran: false
}
2017-12-08 04:54:54 +00:00
update: (obj, ...) =>
unless obj[@u].ran
result = @node\update obj, ...
2017-12-08 04:54:54 +00:00
unless result == running
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
}
newClass = setmetatable {
2017-12-09 16:54:54 +00:00
__init: ->
__base: base
__name: name
}, {
__call: (cls, ...) ->
@ = setmetatable({}, base)
2017-12-09 16:54:54 +00:00
cls.__init(@, ...)
return @
}
if parent
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
setmetatable {
2017-12-08 04:54:54 +00:00
-- Leaf Node
:Node
-- Composite Nodes
:Sequence
:Selector
:Random
:RandomSequence
:RandomSelector
-- Decorator Nodes
: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
}, {
__call: (bt, ...) ->
bt.Sequence ...
2017-12-08 04:54:54 +00:00
}