diff --git a/README.md b/README.md index 7298d24e..2166b43d 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,10 @@ In order to create logic flows based on conditions and actions, we need to _comp The **Selector Star** node is similar to the **Selector**, however, it will skip all previous child nodes that were executed prior, in case one of the children is currently in `RUNNING` state. A usecase for this is if you want to ensure that only one action is executed at a time, regardless of for long it runs. +### ![icon](addons/beehave/icons/selector_random.svg) Selector Random + +The **Selector Random** will attempt to execute all of its children just like a **Selector Star** would, with the exception that the children will be executed in a random order. + ### ![icon](addons/beehave/icons/sequence.svg) Sequence **Sequence** nodes will attempt to execute all of its children and reports `SUCCESS` status code in case all of the children report a `SUCCESS` status code. In case at least one child reports a `FAILURE` status code, this node will also return `FAILURE` status code. This node will attempt to process all its children every single tick, even if one of them is currently `RUNNING` already. @@ -95,6 +99,10 @@ The **Selector Star** node is similar to the **Selector**, however, it will skip The **Sequence Star** node is similar to the **Sequence**, however, it will skip all previous child nodes that succeeded prior, in case one of the children is currently in `RUNNING` state. A usecase for this is if you want to ensure that only one action is executed at a time, regardless of for long it runs. +### ![icon](addons/beehave/icons/sequence_random.svg) Sequence Random + +The **Sequence Random** will attempt to execute all of its children just like a **Sequence Star** would, with the exception that the children will be executed in a random order. + ## ![icon](addons/beehave/icons/category_leaf.svg) Decorators **Decorators** are nodes that can be used in combination with any other node described above. diff --git a/addons/beehave/icons/selector_random.svg b/addons/beehave/icons/selector_random.svg new file mode 100644 index 00000000..6f631e9a --- /dev/null +++ b/addons/beehave/icons/selector_random.svg @@ -0,0 +1,35 @@ + + diff --git a/addons/beehave/icons/sequence_random.svg b/addons/beehave/icons/sequence_random.svg new file mode 100644 index 00000000..34e4a12a --- /dev/null +++ b/addons/beehave/icons/sequence_random.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/nodes/composites/selector_random.gd b/addons/beehave/nodes/composites/selector_random.gd new file mode 100644 index 00000000..b1d90e25 --- /dev/null +++ b/addons/beehave/nodes/composites/selector_random.gd @@ -0,0 +1,54 @@ +## This node will attempt to execute all of its children just like a +## [code]SelectorStar[/code] would, with the exception that the children +## will be executed in a random order. +@tool +class_name SelectorRandomComposite extends Composite +@icon("../../icons/selector_random.svg") + + +## A shuffled list of the children that will be executed in reverse order. +var _children_bag: Array[Node] = [] +var c: Node + + +func tick(actor: Node, blackboard: Blackboard) -> int: + if _children_bag.is_empty(): + _reset() + + # We need to traverse the array in reverse since we will be manipulating it. + for i in _get_reversed_indexes(): + c = _children_bag[i] + var response = c.tick(actor, blackboard) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c) + blackboard.set_value("last_condition_status", response) + + if response == RUNNING: + running_child = c + else: + _children_bag.erase(c) + + if response != FAILURE: + if response == SUCCESS: + _reset() + return response + + return FAILURE + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + + +func _get_reversed_indexes() -> Array[int]: + var reversed = range(_children_bag.size()) + reversed.reverse() + return reversed + + +## Generates a new shuffled list of the children. +func _reset() -> void: + _children_bag = get_children().duplicate() + _children_bag.shuffle() diff --git a/addons/beehave/nodes/composites/sequence_random.gd b/addons/beehave/nodes/composites/sequence_random.gd new file mode 100644 index 00000000..b751c31b --- /dev/null +++ b/addons/beehave/nodes/composites/sequence_random.gd @@ -0,0 +1,60 @@ +## This node will attempt to execute all of its children just like a +## [code]SequenceStar[/code] would, with the exception that the children +## will be executed in a random order. +@tool +class_name SequenceRandomComposite extends Composite +@icon("../../icons/sequence_random.svg") + + +## Whether the sequence should start where it left off after a previous failure. +@export var resume_on_failure: bool = false +## Whether the sequence should start where it left off after a previous interruption. +@export var resume_on_interrupt: bool = false + +## A shuffled list of the children that will be executed in reverse order. +var _children_bag: Array[Node] = [] +var c: Node + + +func tick(actor: Node, blackboard: Blackboard) -> int: + if _children_bag.is_empty(): + _reset() + + # We need to traverse the array in reverse since we will be manipulating it. + for i in _get_reversed_indexes(): + c = _children_bag[i] + var response = c.tick(actor, blackboard) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c) + blackboard.set_value("last_condition_status", response) + + if response == RUNNING: + running_child = c + else: + _children_bag.erase(c) + + if response != SUCCESS: + if not resume_on_failure and response == FAILURE: + _reset() + return response + + return SUCCESS + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + if not resume_on_interrupt: + _reset() + super(actor, blackboard) + + +func _get_reversed_indexes() -> Array[int]: + var reversed = range(_children_bag.size()) + reversed.reverse() + return reversed + + +## Generates a new shuffled list of the children. +func _reset() -> void: + _children_bag = get_children().duplicate() + _children_bag.shuffle()