Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SimpleParallelComposite should have action leaf as first child node #339

Open
Horryportier opened this issue May 30, 2024 · 2 comments
Open

Comments

@Horryportier
Copy link

Is your feature request related to a problem? Please describe.
I'm wondering is there an reason why this node can't have anything else than action node as first child. I've looked into code of the node and i can't seam to find reason why other types can't be used (although I'm not familiar with this code base). If there is an good reason for that then I would like to know why that is the case

Describe the solution you'd like
SimpleParallelComposite that can run other types of nodes

Describe alternatives you've considered
N/A

Additional context
N/A

@Flynsarmy
Copy link
Contributor

Flynsarmy commented Jan 16, 2025

I made a RepeatableParallelComposite that works the same way as LimboAI's. Supports unlimited children, no strange no restrictions etc. Needs more testing but this should get you started.

@tool
@icon("res://addons/beehave/icons/simple_parallel.svg")

## Executes each child sequentially each tick.[br]
## Gathers the responses and if [code]num_failures_required[/code] or [code]num_successes_required[/code]
## is reached, returns [code]FAILURE[/code] or [code]SUCCESS[/code]. Returns [code]FAILURE[/code] if both are met.[br]
## If set to [code]repeat[/code], all child tasks will be re-executed each tick, regardless of whether
## they previously resulted in [code]SUCCESS[/code] or [code]FAILURE[/code]
class_name RepeatableParallelComposite extends Composite

## If the specified number of child tasks return SUCCESS, RepeatableParallel will also return SUCCESS.
@export_range(1, 50) var num_failures_required: int = 1
## If the specified number of child tasks return FAILURE, RepeatableParallel will also return FAILURE.
@export_range(1, 50) var num_successes_required: int = 1
## When true, the child tasks will be executed again, regardless of whether they previously resulted in a SUCCESS or FAILURE.[br]
## When false, if none of the criteria were met, and all child tasks resulted in a SUCCESS or FAILURE, RepeatableParallel will return FAILURE.
@export var repeat: bool = false

var responses: Array[int]

func _get_configuration_warnings() -> PackedStringArray:
	var warnings: PackedStringArray = super._get_configuration_warnings()

	if get_child_count() < 2:
		warnings.append("RepeatableParallel should have at least two child nodes.")

	return warnings

## Called before the first time it ticks by the parent.
func before_run(_actor: Node, _blackboard: Blackboard) -> void:
	responses.resize(get_child_count())
	responses.fill(-1)

func tick(actor, blackboard: Blackboard):
	var num_successes: int = 0
	var num_failures: int = 0
	var return_status: int = RUNNING

	for child in get_children():
		var index: int = child.get_index()
		var response: int

		# This child failed/succeeded last tick. We don't need to re-execute them.
		if (not repeat and (responses[index] == FAILURE or responses[index] == SUCCESS)):
			response = responses[index]
		# Run the child
		else:
			# Only before_run once per child
			if responses[index] == -1:
				child.before_run(actor, blackboard)
			response = child.tick(actor, blackboard)
			responses[index] = response
			if can_send_message(blackboard):
				BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response)
			if response != RUNNING:
				child.after_run(actor, blackboard)

		if response == FAILURE:
			num_failures += 1
			if num_failures >= num_failures_required and return_status == RUNNING:
				return_status = FAILURE
		elif response == SUCCESS:
			num_successes += 1
			if num_successes >= num_successes_required and return_status == RUNNING:
				return_status = SUCCESS

	var total_counted: int = num_failures + num_successes
	if not repeat and (total_counted == get_child_count()) and return_status == RUNNING:
		return_status = FAILURE

	return return_status

@bitbrain
Copy link
Owner

@Flynsarmy feel free to raise a PR for this! Please don't forget about unit tests, too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants