generated from krampus/template-godot4
	basic_game #1
							
								
								
									
										21
									
								
								addons/beehave/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								addons/beehave/LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | MIT License | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2023 bitbrain | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
							
								
								
									
										51
									
								
								addons/beehave/blackboard.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								addons/beehave/blackboard.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | @icon("icons/blackboard.svg") | ||||||
|  | class_name Blackboard extends Node | ||||||
|  | 
 | ||||||
|  | const DEFAULT = "default" | ||||||
|  | 
 | ||||||
|  | ## The blackboard is an object that can be used to store and access data between | ||||||
|  | ## multiple nodes of the behavior tree. | ||||||
|  | @export var blackboard: Dictionary = {}: | ||||||
|  | 	set(b): | ||||||
|  | 		blackboard = b | ||||||
|  | 		_data[DEFAULT] = blackboard | ||||||
|  | 
 | ||||||
|  | var _data: Dictionary = {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _ready(): | ||||||
|  | 	_data[DEFAULT] = blackboard | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func keys() -> Array[String]: | ||||||
|  | 	var keys: Array[String] | ||||||
|  | 	keys.assign(_data.keys().duplicate()) | ||||||
|  | 	return keys | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_value(key: Variant, value: Variant, blackboard_name: String = DEFAULT) -> void: | ||||||
|  | 	if not _data.has(blackboard_name): | ||||||
|  | 		_data[blackboard_name] = {} | ||||||
|  | 
 | ||||||
|  | 	_data[blackboard_name][key] = value | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_value( | ||||||
|  | 	key: Variant, default_value: Variant = null, blackboard_name: String = DEFAULT | ||||||
|  | ) -> Variant: | ||||||
|  | 	if has_value(key, blackboard_name): | ||||||
|  | 		return _data[blackboard_name].get(key, default_value) | ||||||
|  | 	return default_value | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func has_value(key: Variant, blackboard_name: String = DEFAULT) -> bool: | ||||||
|  | 	return ( | ||||||
|  | 		_data.has(blackboard_name) | ||||||
|  | 		and _data[blackboard_name].has(key) | ||||||
|  | 		and _data[blackboard_name][key] != null | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func erase_value(key: Variant, blackboard_name: String = DEFAULT) -> void: | ||||||
|  | 	if _data.has(blackboard_name): | ||||||
|  | 		_data[blackboard_name][key] = null | ||||||
							
								
								
									
										96
									
								
								addons/beehave/debug/debugger.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								addons/beehave/debug/debugger.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,96 @@ | |||||||
|  | @tool | ||||||
|  | extends EditorDebuggerPlugin | ||||||
|  | 
 | ||||||
|  | const DebuggerTab := preload("debugger_tab.gd") | ||||||
|  | const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") | ||||||
|  | 
 | ||||||
|  | var debugger_tab := DebuggerTab.new() | ||||||
|  | var floating_window: Window | ||||||
|  | var session: EditorDebuggerSession | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _has_capture(prefix: String) -> bool: | ||||||
|  | 	return prefix == "beehave" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _capture(message: String, data: Array, session_id: int) -> bool: | ||||||
|  | 	# in case the behavior tree has invalid setup this might be null | ||||||
|  | 	if debugger_tab == null: | ||||||
|  | 		return false | ||||||
|  | 
 | ||||||
|  | 	if message == "beehave:register_tree": | ||||||
|  | 		debugger_tab.register_tree(data[0]) | ||||||
|  | 		return true | ||||||
|  | 	if message == "beehave:unregister_tree": | ||||||
|  | 		debugger_tab.unregister_tree(data[0]) | ||||||
|  | 		return true | ||||||
|  | 	if message == "beehave:process_tick": | ||||||
|  | 		debugger_tab.graph.process_tick(data[0], data[1]) | ||||||
|  | 		return true | ||||||
|  | 	if message == "beehave:process_begin": | ||||||
|  | 		debugger_tab.graph.process_begin(data[0]) | ||||||
|  | 		return true | ||||||
|  | 	if message == "beehave:process_end": | ||||||
|  | 		debugger_tab.graph.process_end(data[0]) | ||||||
|  | 		return true | ||||||
|  | 	return false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _setup_session(session_id: int) -> void: | ||||||
|  | 	session = get_session(session_id) | ||||||
|  | 	session.started.connect(debugger_tab.start) | ||||||
|  | 	session.stopped.connect(debugger_tab.stop) | ||||||
|  | 
 | ||||||
|  | 	debugger_tab.name = "🐝 Beehave" | ||||||
|  | 	debugger_tab.make_floating.connect(_on_make_floating) | ||||||
|  | 	debugger_tab.session = session | ||||||
|  | 	session.add_session_tab(debugger_tab) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_make_floating() -> void: | ||||||
|  | 	var plugin := BeehaveUtils.get_plugin() | ||||||
|  | 	if not plugin: | ||||||
|  | 		return | ||||||
|  | 	if floating_window: | ||||||
|  | 		_on_window_close_requested() | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	var border_size := Vector2(4, 4) * BeehaveUtils.get_editor_scale() | ||||||
|  | 	var editor_interface: EditorInterface = plugin.get_editor_interface() | ||||||
|  | 	var editor_main_screen = editor_interface.get_editor_main_screen() | ||||||
|  | 	debugger_tab.get_parent().remove_child(debugger_tab) | ||||||
|  | 
 | ||||||
|  | 	floating_window = Window.new() | ||||||
|  | 
 | ||||||
|  | 	var panel := Panel.new() | ||||||
|  | 	panel.add_theme_stylebox_override( | ||||||
|  | 		"panel", | ||||||
|  | 		editor_interface.get_base_control().get_theme_stylebox("PanelForeground", "EditorStyles") | ||||||
|  | 	) | ||||||
|  | 	panel.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) | ||||||
|  | 	floating_window.add_child(panel) | ||||||
|  | 
 | ||||||
|  | 	var margin := MarginContainer.new() | ||||||
|  | 	margin.add_child(debugger_tab) | ||||||
|  | 	margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) | ||||||
|  | 	margin.add_theme_constant_override("margin_right", border_size.x) | ||||||
|  | 	margin.add_theme_constant_override("margin_left", border_size.x) | ||||||
|  | 	margin.add_theme_constant_override("margin_top", border_size.y) | ||||||
|  | 	margin.add_theme_constant_override("margin_bottom", border_size.y) | ||||||
|  | 	panel.add_child(margin) | ||||||
|  | 
 | ||||||
|  | 	floating_window.title = "🐝 Beehave" | ||||||
|  | 	floating_window.wrap_controls = true | ||||||
|  | 	floating_window.min_size = Vector2i(600, 350) | ||||||
|  | 	floating_window.size = debugger_tab.size | ||||||
|  | 	floating_window.position = editor_main_screen.global_position | ||||||
|  | 	floating_window.transient = true | ||||||
|  | 	floating_window.close_requested.connect(_on_window_close_requested) | ||||||
|  | 	editor_interface.get_base_control().add_child(floating_window) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_window_close_requested() -> void: | ||||||
|  | 	debugger_tab.get_parent().remove_child(debugger_tab) | ||||||
|  | 	session.add_session_tab(debugger_tab) | ||||||
|  | 	floating_window.queue_free() | ||||||
|  | 	floating_window = null | ||||||
							
								
								
									
										30
									
								
								addons/beehave/debug/debugger_messages.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								addons/beehave/debug/debugger_messages.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | class_name BeehaveDebuggerMessages | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static func can_send_message() -> bool: | ||||||
|  | 	return not Engine.is_editor_hint() and OS.has_feature("editor") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static func register_tree(beehave_tree: Dictionary) -> void: | ||||||
|  | 	if can_send_message(): | ||||||
|  | 		EngineDebugger.send_message("beehave:register_tree", [beehave_tree]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static func unregister_tree(instance_id: int) -> void: | ||||||
|  | 	if can_send_message(): | ||||||
|  | 		EngineDebugger.send_message("beehave:unregister_tree", [instance_id]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static func process_tick(instance_id: int, status: int) -> void: | ||||||
|  | 	if can_send_message(): | ||||||
|  | 		EngineDebugger.send_message("beehave:process_tick", [instance_id, status]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static func process_begin(instance_id: int) -> void: | ||||||
|  | 	if can_send_message(): | ||||||
|  | 		EngineDebugger.send_message("beehave:process_begin", [instance_id]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static func process_end(instance_id: int) -> void: | ||||||
|  | 	if can_send_message(): | ||||||
|  | 		EngineDebugger.send_message("beehave:process_end", [instance_id]) | ||||||
							
								
								
									
										125
									
								
								addons/beehave/debug/debugger_tab.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								addons/beehave/debug/debugger_tab.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,125 @@ | |||||||
|  | @tool | ||||||
|  | class_name BeehaveDebuggerTab extends PanelContainer | ||||||
|  | 
 | ||||||
|  | const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") | ||||||
|  | 
 | ||||||
|  | signal make_floating | ||||||
|  | 
 | ||||||
|  | const OldBeehaveGraphEdit := preload("old_graph_edit.gd") | ||||||
|  | const NewBeehaveGraphEdit := preload("new_graph_edit.gd") | ||||||
|  | 
 | ||||||
|  | const TREE_ICON := preload("../icons/tree.svg") | ||||||
|  | 
 | ||||||
|  | var graph | ||||||
|  | var container: HSplitContainer | ||||||
|  | var item_list: ItemList | ||||||
|  | var message: Label | ||||||
|  | 
 | ||||||
|  | var active_trees: Dictionary | ||||||
|  | var active_tree_id: int = -1 | ||||||
|  | var session: EditorDebuggerSession | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _ready() -> void: | ||||||
|  | 	container = HSplitContainer.new() | ||||||
|  | 	add_child(container) | ||||||
|  | 
 | ||||||
|  | 	item_list = ItemList.new() | ||||||
|  | 	item_list.custom_minimum_size = Vector2(200, 0) | ||||||
|  | 	item_list.item_selected.connect(_on_item_selected) | ||||||
|  | 	container.add_child(item_list) | ||||||
|  | 	if Engine.get_version_info().minor >= 2: | ||||||
|  | 		graph = NewBeehaveGraphEdit.new(BeehaveUtils.get_frames()) | ||||||
|  | 	else: | ||||||
|  | 		graph = OldBeehaveGraphEdit.new(BeehaveUtils.get_frames()) | ||||||
|  | 
 | ||||||
|  | 	container.add_child(graph) | ||||||
|  | 
 | ||||||
|  | 	message = Label.new() | ||||||
|  | 	message.text = "Run Project for debugging" | ||||||
|  | 	message.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER | ||||||
|  | 	message.vertical_alignment = VERTICAL_ALIGNMENT_CENTER | ||||||
|  | 	message.set_anchors_preset(Control.PRESET_CENTER) | ||||||
|  | 	add_child(message) | ||||||
|  | 
 | ||||||
|  | 	var button := Button.new() | ||||||
|  | 	button.flat = true | ||||||
|  | 	button.name = "MakeFloatingButton" | ||||||
|  | 	button.icon = get_theme_icon(&"ExternalLink", &"EditorIcons") | ||||||
|  | 	button.pressed.connect(func(): make_floating.emit()) | ||||||
|  | 	button.tooltip_text = "Make floating" | ||||||
|  | 	button.focus_mode = Control.FOCUS_NONE | ||||||
|  | 	graph.get_menu_container().add_child(button) | ||||||
|  | 
 | ||||||
|  | 	var toggle_button := Button.new() | ||||||
|  | 	toggle_button.flat = true | ||||||
|  | 	toggle_button.name = "TogglePanelButton" | ||||||
|  | 	toggle_button.icon = get_theme_icon(&"Back", &"EditorIcons") | ||||||
|  | 	toggle_button.pressed.connect(_on_toggle_button_pressed.bind(toggle_button)) | ||||||
|  | 	toggle_button.tooltip_text = "Toggle Panel" | ||||||
|  | 	toggle_button.focus_mode = Control.FOCUS_NONE | ||||||
|  | 	graph.get_menu_container().add_child(toggle_button) | ||||||
|  | 	graph.get_menu_container().move_child(toggle_button, 0) | ||||||
|  | 
 | ||||||
|  | 	stop() | ||||||
|  | 	visibility_changed.connect(_on_visibility_changed) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func start() -> void: | ||||||
|  | 	container.visible = true | ||||||
|  | 	message.visible = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func stop() -> void: | ||||||
|  | 	container.visible = false | ||||||
|  | 	message.visible = true | ||||||
|  | 
 | ||||||
|  | 	active_trees.clear() | ||||||
|  | 	item_list.clear() | ||||||
|  | 	graph.beehave_tree = {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func register_tree(data: Dictionary) -> void: | ||||||
|  | 	if not active_trees.has(data.id): | ||||||
|  | 		var idx := item_list.add_item(data.name, TREE_ICON) | ||||||
|  | 		item_list.set_item_tooltip(idx, data.path) | ||||||
|  | 		item_list.set_item_metadata(idx, data.id) | ||||||
|  | 
 | ||||||
|  | 	active_trees[data.id] = data | ||||||
|  | 
 | ||||||
|  | 	if active_tree_id == data.id.to_int(): | ||||||
|  | 		graph.beehave_tree = data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func unregister_tree(instance_id: int) -> void: | ||||||
|  | 	var id := str(instance_id) | ||||||
|  | 	for i in item_list.item_count: | ||||||
|  | 		if item_list.get_item_metadata(i) == id: | ||||||
|  | 			item_list.remove_item(i) | ||||||
|  | 			break | ||||||
|  | 
 | ||||||
|  | 	active_trees.erase(id) | ||||||
|  | 
 | ||||||
|  | 	if graph.beehave_tree.get("id", "") == id: | ||||||
|  | 		graph.beehave_tree = {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_toggle_button_pressed(toggle_button: Button) -> void: | ||||||
|  | 	item_list.visible = !item_list.visible | ||||||
|  | 	toggle_button.icon = get_theme_icon( | ||||||
|  | 		&"Back" if item_list.visible else &"Forward", &"EditorIcons" | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_item_selected(idx: int) -> void: | ||||||
|  | 	var id: StringName = item_list.get_item_metadata(idx) | ||||||
|  | 	graph.beehave_tree = active_trees.get(id, {}) | ||||||
|  | 
 | ||||||
|  | 	active_tree_id = id.to_int() | ||||||
|  | 	if session != null: | ||||||
|  | 		session.send_message("beehave:activate_tree", [active_tree_id]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_visibility_changed() -> void: | ||||||
|  | 	if session != null: | ||||||
|  | 		session.send_message("beehave:visibility_changed", [visible and is_visible_in_tree()]) | ||||||
							
								
								
									
										38
									
								
								addons/beehave/debug/global_debugger.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/debug/global_debugger.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | extends Node | ||||||
|  | 
 | ||||||
|  | var _registered_trees: Dictionary | ||||||
|  | var _active_tree | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _enter_tree() -> void: | ||||||
|  | 	EngineDebugger.register_message_capture("beehave", _on_debug_message) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_debug_message(message: String, data: Array) -> bool: | ||||||
|  | 	if message == "activate_tree": | ||||||
|  | 		_set_active_tree(data[0]) | ||||||
|  | 		return true | ||||||
|  | 	if message == "visibility_changed": | ||||||
|  | 		if _active_tree && is_instance_valid(_active_tree): | ||||||
|  | 			_active_tree._can_send_message = data[0] | ||||||
|  | 		return true | ||||||
|  | 	return false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _set_active_tree(tree_id: int) -> void: | ||||||
|  | 	var tree = _registered_trees.get(tree_id, null) | ||||||
|  | 	if not tree: | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	if _active_tree && is_instance_valid(_active_tree): | ||||||
|  | 		_active_tree._can_send_message = false | ||||||
|  | 	_active_tree = tree | ||||||
|  | 	_active_tree._can_send_message = true | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func register_tree(tree) -> void: | ||||||
|  | 	_registered_trees[tree.get_instance_id()] = tree | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func unregister_tree(tree) -> void: | ||||||
|  | 	_registered_trees.erase(tree.get_instance_id()) | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/horizontal_layout.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/horizontal_layout.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/debug/icons/horizontal_layout.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/debug/icons/horizontal_layout.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://bah77esichnyx" | ||||||
|  | path="res://.godot/imported/horizontal_layout.svg-d2a7af351e44f9bf61d0c938b6f47fac.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/debug/icons/horizontal_layout.svg" | ||||||
|  | dest_files=["res://.godot/imported/horizontal_layout.svg-d2a7af351e44f9bf61d0c938b6f47fac.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/port_bottom.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/port_bottom.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/debug/icons/port_bottom.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/debug/icons/port_bottom.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://da3b236rjbqns" | ||||||
|  | path="res://.godot/imported/port_bottom.svg-e5c5c61b642a79ab9c2b66ff56603d34.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/debug/icons/port_bottom.svg" | ||||||
|  | dest_files=["res://.godot/imported/port_bottom.svg-e5c5c61b642a79ab9c2b66ff56603d34.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/port_left.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/port_left.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/debug/icons/port_left.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/debug/icons/port_left.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://bnufc8p6spdtn" | ||||||
|  | path="res://.godot/imported/port_left.svg-69cd927c4db555f1edbb8d1f553ea2fd.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/debug/icons/port_left.svg" | ||||||
|  | dest_files=["res://.godot/imported/port_left.svg-69cd927c4db555f1edbb8d1f553ea2fd.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/port_right.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/port_right.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/debug/icons/port_right.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/debug/icons/port_right.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://bbmd6vk23ympm" | ||||||
|  | path="res://.godot/imported/port_right.svg-f760bd8be2dd613d0d3848c998c92a2a.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/debug/icons/port_right.svg" | ||||||
|  | dest_files=["res://.godot/imported/port_right.svg-f760bd8be2dd613d0d3848c998c92a2a.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/port_top.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/port_top.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/debug/icons/port_top.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/debug/icons/port_top.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://bw8wmxdfom8eh" | ||||||
|  | path="res://.godot/imported/port_top.svg-d1b336cdc6a0dd570305782a1e56f61d.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/debug/icons/port_top.svg" | ||||||
|  | dest_files=["res://.godot/imported/port_top.svg-d1b336cdc6a0dd570305782a1e56f61d.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/vertical_layout.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/debug/icons/vertical_layout.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/debug/icons/vertical_layout.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/debug/icons/vertical_layout.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://bpyxu6i1dx5qh" | ||||||
|  | path="res://.godot/imported/vertical_layout.svg-1a08fee4b09812a05bcf3defb8afcc4c.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/debug/icons/vertical_layout.svg" | ||||||
|  | dest_files=["res://.godot/imported/vertical_layout.svg-1a08fee4b09812a05bcf3defb8afcc4c.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										69
									
								
								addons/beehave/debug/new_frames.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								addons/beehave/debug/new_frames.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | |||||||
|  | @tool | ||||||
|  | extends RefCounted | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const SUCCESS_COLOR := Color("#07783a") | ||||||
|  | const NORMAL_COLOR := Color("#15181e") | ||||||
|  | const FAILURE_COLOR := Color("#82010b") | ||||||
|  | const RUNNING_COLOR := Color("#c29c06") | ||||||
|  | 
 | ||||||
|  | var panel_normal: StyleBoxFlat | ||||||
|  | var panel_success: StyleBoxFlat | ||||||
|  | var panel_failure: StyleBoxFlat | ||||||
|  | var panel_running: StyleBoxFlat | ||||||
|  | 
 | ||||||
|  | var titlebar_normal: StyleBoxFlat | ||||||
|  | var titlebar_success: StyleBoxFlat | ||||||
|  | var titlebar_failure: StyleBoxFlat | ||||||
|  | var titlebar_running: StyleBoxFlat | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _init() -> void: | ||||||
|  | 	var plugin := BeehaveUtils.get_plugin() | ||||||
|  | 	if not plugin: | ||||||
|  | 		return | ||||||
|  | 	 | ||||||
|  | 	 | ||||||
|  | 	titlebar_normal = ( | ||||||
|  | 		plugin | ||||||
|  | 		.get_editor_interface() | ||||||
|  | 		.get_base_control() | ||||||
|  | 		.get_theme_stylebox(&"titlebar", &"GraphNode")\ | ||||||
|  | 		.duplicate() | ||||||
|  | 	) | ||||||
|  | 	titlebar_success = titlebar_normal.duplicate() | ||||||
|  | 	titlebar_failure = titlebar_normal.duplicate() | ||||||
|  | 	titlebar_running = titlebar_normal.duplicate() | ||||||
|  | 	 | ||||||
|  | 	titlebar_success.bg_color = SUCCESS_COLOR | ||||||
|  | 	titlebar_failure.bg_color = FAILURE_COLOR | ||||||
|  | 	titlebar_running.bg_color = RUNNING_COLOR | ||||||
|  | 	 | ||||||
|  | 	titlebar_success.border_color = SUCCESS_COLOR | ||||||
|  | 	titlebar_failure.border_color = FAILURE_COLOR | ||||||
|  | 	titlebar_running.border_color = RUNNING_COLOR | ||||||
|  | 	 | ||||||
|  | 	 | ||||||
|  | 	panel_normal = ( | ||||||
|  | 		plugin | ||||||
|  | 		.get_editor_interface() | ||||||
|  | 		.get_base_control() | ||||||
|  | 		.get_theme_stylebox(&"panel", &"GraphNode") | ||||||
|  | 		.duplicate() | ||||||
|  | 	) | ||||||
|  | 	panel_success = ( | ||||||
|  | 		plugin | ||||||
|  | 		.get_editor_interface() | ||||||
|  | 		.get_base_control() | ||||||
|  | 		.get_theme_stylebox(&"panel_selected", &"GraphNode") | ||||||
|  | 		.duplicate() | ||||||
|  | 	) | ||||||
|  | 	panel_failure = panel_success.duplicate() | ||||||
|  | 	panel_running = panel_success.duplicate() | ||||||
|  | 	 | ||||||
|  | 	panel_success.border_color = SUCCESS_COLOR | ||||||
|  | 	panel_failure.border_color = FAILURE_COLOR | ||||||
|  | 	panel_running.border_color = RUNNING_COLOR | ||||||
							
								
								
									
										296
									
								
								addons/beehave/debug/new_graph_edit.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								addons/beehave/debug/new_graph_edit.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,296 @@ | |||||||
|  | @tool | ||||||
|  | extends GraphEdit | ||||||
|  | 
 | ||||||
|  | const BeehaveGraphNode := preload("new_graph_node.gd") | ||||||
|  | 
 | ||||||
|  | const HORIZONTAL_LAYOUT_ICON := preload("icons/horizontal_layout.svg") | ||||||
|  | const VERTICAL_LAYOUT_ICON := preload("icons/vertical_layout.svg") | ||||||
|  | 
 | ||||||
|  | const PROGRESS_SHIFT: int = 50 | ||||||
|  | const INACTIVE_COLOR: Color = Color("#898989") | ||||||
|  | const ACTIVE_COLOR: Color = Color("#c29c06") | ||||||
|  | const SUCCESS_COLOR: Color = Color("#07783a") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | var updating_graph: bool = false | ||||||
|  | var arraging_nodes: bool = false | ||||||
|  | var beehave_tree: Dictionary: | ||||||
|  | 	set(value): | ||||||
|  | 		if beehave_tree == value: | ||||||
|  | 			return | ||||||
|  | 		beehave_tree = value | ||||||
|  | 		active_nodes.clear() | ||||||
|  | 		_update_graph() | ||||||
|  | 
 | ||||||
|  | var horizontal_layout: bool = false: | ||||||
|  | 	set(value): | ||||||
|  | 		if updating_graph or arraging_nodes: | ||||||
|  | 			return | ||||||
|  | 		if horizontal_layout == value: | ||||||
|  | 			return | ||||||
|  | 		horizontal_layout = value | ||||||
|  | 		_update_layout_button() | ||||||
|  | 		_update_graph() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | var frames:RefCounted | ||||||
|  | var active_nodes: Array[String] | ||||||
|  | var progress: int = 0 | ||||||
|  | var layout_button: Button | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _init(frames:RefCounted) -> void: | ||||||
|  | 	self.frames = frames | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _ready() -> void: | ||||||
|  | 	custom_minimum_size = Vector2(100, 300) | ||||||
|  | 	set("show_arrange_button", true) | ||||||
|  | 	minimap_enabled = false | ||||||
|  | 	layout_button = Button.new() | ||||||
|  | 	layout_button.flat = true | ||||||
|  | 	layout_button.focus_mode = Control.FOCUS_NONE | ||||||
|  | 	layout_button.pressed.connect(func(): horizontal_layout = not horizontal_layout) | ||||||
|  | 	get_menu_container().add_child(layout_button) | ||||||
|  | 	_update_layout_button() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _update_graph() -> void: | ||||||
|  | 	if updating_graph: | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	updating_graph = true | ||||||
|  | 
 | ||||||
|  | 	clear_connections() | ||||||
|  | 
 | ||||||
|  | 	for child in _get_child_nodes(): | ||||||
|  | 		remove_child(child) | ||||||
|  | 		child.queue_free() | ||||||
|  | 
 | ||||||
|  | 	if not beehave_tree.is_empty(): | ||||||
|  | 		_add_nodes(beehave_tree) | ||||||
|  | 		_connect_nodes(beehave_tree) | ||||||
|  | 		_arrange_nodes.call_deferred(beehave_tree) | ||||||
|  | 
 | ||||||
|  | 	updating_graph = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _add_nodes(node: Dictionary) -> void: | ||||||
|  | 	if node.is_empty(): | ||||||
|  | 		return | ||||||
|  | 	var gnode := BeehaveGraphNode.new(frames, horizontal_layout) | ||||||
|  | 	add_child(gnode) | ||||||
|  | 	gnode.title_text = node.name | ||||||
|  | 	gnode.name = node.id | ||||||
|  | 	gnode.icon = _get_icon(node.type.back()) | ||||||
|  | 
 | ||||||
|  | 	if node.type.has(&"BeehaveTree"): | ||||||
|  | 		gnode.set_slots(false, true) | ||||||
|  | 	elif node.type.has(&"Leaf"): | ||||||
|  | 		gnode.set_slots(true, false) | ||||||
|  | 	elif node.type.has(&"Composite") or node.type.has(&"Decorator"): | ||||||
|  | 		gnode.set_slots(true, true) | ||||||
|  | 
 | ||||||
|  | 	for child in node.get("children", []): | ||||||
|  | 		_add_nodes(child) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _connect_nodes(node: Dictionary) -> void: | ||||||
|  | 	for child in node.get("children", []): | ||||||
|  | 		connect_node(node.id, 0, child.id, 0) | ||||||
|  | 		_connect_nodes(child) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _arrange_nodes(node: Dictionary) -> void: | ||||||
|  | 	if arraging_nodes: | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	arraging_nodes = true | ||||||
|  | 
 | ||||||
|  | 	var tree_node := _create_tree_nodes(node) | ||||||
|  | 	tree_node.update_positions(horizontal_layout) | ||||||
|  | 	_place_nodes(tree_node) | ||||||
|  | 
 | ||||||
|  | 	arraging_nodes = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _create_tree_nodes(node: Dictionary, root: TreeNode = null) -> TreeNode: | ||||||
|  | 	var tree_node := TreeNode.new(get_node(node.id), root) | ||||||
|  | 	for child in node.get("children", []): | ||||||
|  | 		var child_node := _create_tree_nodes(child, tree_node) | ||||||
|  | 		tree_node.children.push_back(child_node) | ||||||
|  | 	return tree_node | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _place_nodes(node: TreeNode) -> void: | ||||||
|  | 	node.item.position_offset = Vector2(node.x, node.y) | ||||||
|  | 	for child in node.children: | ||||||
|  | 		_place_nodes(child) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_icon(type: StringName) -> Texture2D: | ||||||
|  | 	var classes := ProjectSettings.get_global_class_list() | ||||||
|  | 	for c in classes: | ||||||
|  | 		if c["class"] == type: | ||||||
|  | 			var icon_path := c.get("icon", String()) | ||||||
|  | 			if not icon_path.is_empty(): | ||||||
|  | 				return load(icon_path) | ||||||
|  | 	return null | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_menu_container() -> Control: | ||||||
|  | 	return call("get_menu_hbox") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_status(status: int) -> String: | ||||||
|  | 	if status == 0: | ||||||
|  | 		return "SUCCESS" | ||||||
|  | 	elif status == 1: | ||||||
|  | 		return "FAILURE" | ||||||
|  | 	return "RUNNING" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func process_begin(instance_id: int) -> void: | ||||||
|  | 	if not _is_same_tree(instance_id): | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	for child in _get_child_nodes(): | ||||||
|  | 		child.set_meta("status", -1) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func process_tick(instance_id: int, status: int) -> void: | ||||||
|  | 	var node := get_node_or_null(str(instance_id)) | ||||||
|  | 	if node: | ||||||
|  | 		node.text = "Status: %s" % get_status(status) | ||||||
|  | 		node.set_status(status) | ||||||
|  | 		node.set_meta("status", status) | ||||||
|  | 		if status == 0 or status == 2: | ||||||
|  | 			if not active_nodes.has(node.name): | ||||||
|  | 				active_nodes.push_back(node.name) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func process_end(instance_id: int) -> void: | ||||||
|  | 	if not _is_same_tree(instance_id): | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	for child in _get_child_nodes(): | ||||||
|  | 		var status := child.get_meta("status", -1) | ||||||
|  | 		match status: | ||||||
|  | 			0: | ||||||
|  | 				active_nodes.erase(child.name) | ||||||
|  | 				child.set_color(SUCCESS_COLOR) | ||||||
|  | 			1: | ||||||
|  | 				active_nodes.erase(child.name) | ||||||
|  | 				child.set_color(INACTIVE_COLOR) | ||||||
|  | 			2: | ||||||
|  | 				child.set_color(ACTIVE_COLOR) | ||||||
|  | 			_: | ||||||
|  | 				child.text = " " | ||||||
|  | 				child.set_status(status) | ||||||
|  | 				child.set_color(INACTIVE_COLOR) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _is_same_tree(instance_id: int) -> bool: | ||||||
|  | 	return str(instance_id) == beehave_tree.get("id", "") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_child_nodes() -> Array[Node]: | ||||||
|  | 	return get_children().filter(func(child): return child is BeehaveGraphNode) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_connection_line(from_position: Vector2, to_position: Vector2) -> PackedVector2Array: | ||||||
|  | 	for child in _get_child_nodes(): | ||||||
|  | 		for port in child.get_input_port_count(): | ||||||
|  | 			if not (child.position_offset + child.get_input_port_position(port)).is_equal_approx(to_position): | ||||||
|  | 				continue | ||||||
|  | 			to_position = child.position_offset + child.get_custom_input_port_position(horizontal_layout) | ||||||
|  | 		for port in child.get_output_port_count(): | ||||||
|  | 			if not (child.position_offset + child.get_output_port_position(port)).is_equal_approx(from_position): | ||||||
|  | 				continue | ||||||
|  | 			from_position = child.position_offset + child.get_custom_output_port_position(horizontal_layout) | ||||||
|  | 	return _get_elbow_connection_line(from_position, to_position) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_elbow_connection_line(from_position: Vector2, to_position: Vector2) -> PackedVector2Array: | ||||||
|  | 	var points: PackedVector2Array | ||||||
|  | 	 | ||||||
|  | 	points.push_back(from_position) | ||||||
|  | 
 | ||||||
|  | 	var mid_position := ((to_position + from_position) / 2).round() | ||||||
|  | 	if horizontal_layout: | ||||||
|  | 		points.push_back(Vector2(mid_position.x, from_position.y)) | ||||||
|  | 		points.push_back(Vector2(mid_position.x, to_position.y)) | ||||||
|  | 	else: | ||||||
|  | 		points.push_back(Vector2(from_position.x, mid_position.y)) | ||||||
|  | 		points.push_back(Vector2(to_position.x, mid_position.y)) | ||||||
|  | 
 | ||||||
|  | 	points.push_back(to_position) | ||||||
|  | 	 | ||||||
|  | 	return points | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _process(delta: float) -> void: | ||||||
|  | 	if not active_nodes.is_empty(): | ||||||
|  | 		progress += 10 if delta >= 0.05 else 1 | ||||||
|  | 		if progress >= 1000: | ||||||
|  | 			progress = 0 | ||||||
|  | 		queue_redraw() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _draw() -> void: | ||||||
|  | 	if active_nodes.is_empty(): | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	var circle_size: float = max(3, 6 * zoom) | ||||||
|  | 	var progress_shift: float = PROGRESS_SHIFT * zoom | ||||||
|  | 
 | ||||||
|  | 	var connections := get_connection_list() | ||||||
|  | 	for c in connections: | ||||||
|  | 		var from_node: StringName | ||||||
|  | 		var to_node: StringName | ||||||
|  | 
 | ||||||
|  | 		from_node = c.from_node | ||||||
|  | 		to_node = c.to_node | ||||||
|  | 
 | ||||||
|  | 		if not from_node in active_nodes or not c.to_node in active_nodes: | ||||||
|  | 			continue | ||||||
|  | 
 | ||||||
|  | 		var from := get_node(String(from_node)) | ||||||
|  | 		var to := get_node(String(to_node)) | ||||||
|  | 
 | ||||||
|  | 		if from.get_meta("status", -1) < 0 or to.get_meta("status", -1) < 0: | ||||||
|  | 			return | ||||||
|  | 
 | ||||||
|  | 		var output_port_position: Vector2 | ||||||
|  | 		var input_port_position: Vector2 | ||||||
|  | 
 | ||||||
|  | 		var scale_factor: float = from.get_rect().size.x / from.size.x | ||||||
|  | 		 | ||||||
|  | 		var line := _get_elbow_connection_line( | ||||||
|  | 			from.position + from.get_custom_output_port_position(horizontal_layout) * scale_factor, | ||||||
|  | 			to.position + to.get_custom_input_port_position(horizontal_layout) * scale_factor | ||||||
|  | 		) | ||||||
|  | 		 | ||||||
|  | 		var curve = Curve2D.new() | ||||||
|  | 		for l in line: | ||||||
|  | 			curve.add_point(l) | ||||||
|  | 
 | ||||||
|  | 		var max_steps := int(curve.get_baked_length()) | ||||||
|  | 		var current_shift := progress % max_steps | ||||||
|  | 		var p := curve.sample_baked(current_shift) | ||||||
|  | 		draw_circle(p, circle_size, ACTIVE_COLOR) | ||||||
|  | 
 | ||||||
|  | 		var shift := current_shift - progress_shift | ||||||
|  | 		while shift >= 0: | ||||||
|  | 			draw_circle(curve.sample_baked(shift), circle_size, ACTIVE_COLOR) | ||||||
|  | 			shift -= progress_shift | ||||||
|  | 
 | ||||||
|  | 		shift = current_shift + progress_shift | ||||||
|  | 		while shift <= curve.get_baked_length(): | ||||||
|  | 			draw_circle(curve.sample_baked(shift), circle_size, ACTIVE_COLOR) | ||||||
|  | 			shift += progress_shift | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _update_layout_button() -> void: | ||||||
|  | 	layout_button.icon = VERTICAL_LAYOUT_ICON if horizontal_layout else HORIZONTAL_LAYOUT_ICON | ||||||
|  | 	layout_button.tooltip_text = "Switch to Vertical layout" if horizontal_layout else "Switch to Horizontal layout" | ||||||
							
								
								
									
										155
									
								
								addons/beehave/debug/new_graph_node.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								addons/beehave/debug/new_graph_node.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,155 @@ | |||||||
|  | @tool | ||||||
|  | extends GraphNode | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") | ||||||
|  | 
 | ||||||
|  | const PORT_TOP_ICON := preload("icons/port_top.svg") | ||||||
|  | const PORT_BOTTOM_ICON := preload("icons/port_bottom.svg") | ||||||
|  | const PORT_LEFT_ICON := preload("icons/port_left.svg") | ||||||
|  | const PORT_RIGHT_ICON := preload("icons/port_right.svg") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @export var title_text: String: | ||||||
|  | 	set(value): | ||||||
|  | 		title_text = value | ||||||
|  | 		if title_label: | ||||||
|  | 			title_label.text = value | ||||||
|  | 
 | ||||||
|  | @export var text: String: | ||||||
|  | 	set(value): | ||||||
|  | 		text = value | ||||||
|  | 		if label: | ||||||
|  | 			label.text = " " if text.is_empty() else text | ||||||
|  | 
 | ||||||
|  | @export var icon: Texture2D: | ||||||
|  | 	set(value): | ||||||
|  | 		icon = value | ||||||
|  | 		if icon_rect: | ||||||
|  | 			icon_rect.texture = value | ||||||
|  | 
 | ||||||
|  | var layout_size: float: | ||||||
|  | 	get: | ||||||
|  | 		return size.y if horizontal else size.x | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | var icon_rect: TextureRect | ||||||
|  | var title_label: Label | ||||||
|  | var label: Label | ||||||
|  | var titlebar_hbox: HBoxContainer | ||||||
|  | 
 | ||||||
|  | var frames: RefCounted | ||||||
|  | var horizontal: bool = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _init(frames:RefCounted, horizontal: bool = false) -> void: | ||||||
|  | 	self.frames = frames | ||||||
|  | 	self.horizontal = horizontal | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _ready() -> void: | ||||||
|  | 	custom_minimum_size = Vector2(50, 50) * BeehaveUtils.get_editor_scale() | ||||||
|  | 	draggable = false | ||||||
|  | 	 | ||||||
|  | 	add_theme_color_override("close_color", Color.TRANSPARENT) | ||||||
|  | 	add_theme_icon_override("close", ImageTexture.new()) | ||||||
|  | 
 | ||||||
|  | 	# For top port | ||||||
|  | 	var top_port: Control = Control.new() | ||||||
|  | 	add_child(top_port) | ||||||
|  | 
 | ||||||
|  | 	icon_rect = TextureRect.new() | ||||||
|  | 	icon_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED | ||||||
|  | 
 | ||||||
|  | 	titlebar_hbox = get_titlebar_hbox() | ||||||
|  | 	titlebar_hbox.get_child(0).queue_free() | ||||||
|  | 	titlebar_hbox.alignment = BoxContainer.ALIGNMENT_BEGIN | ||||||
|  | 	titlebar_hbox.add_child(icon_rect) | ||||||
|  | 
 | ||||||
|  | 	title_label = Label.new() | ||||||
|  | 	title_label.add_theme_color_override("font_color", Color.WHITE) | ||||||
|  | 	var title_font: Font = get_theme_font("title_font").duplicate() | ||||||
|  | 	if title_font is FontVariation: | ||||||
|  | 		title_font.variation_embolden = 1 | ||||||
|  | 	elif title_font is FontFile: | ||||||
|  | 		title_font.font_weight = 700 | ||||||
|  | 	title_label.add_theme_font_override("font", title_font) | ||||||
|  | 	title_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER | ||||||
|  | 	title_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL | ||||||
|  | 	title_label.text = title_text | ||||||
|  | 	titlebar_hbox.add_child(title_label) | ||||||
|  | 
 | ||||||
|  | 	label = Label.new() | ||||||
|  | 	label.text = " " if text.is_empty() else text | ||||||
|  | 	add_child(label) | ||||||
|  | 
 | ||||||
|  | 	# For bottom port | ||||||
|  | 	add_child(Control.new()) | ||||||
|  | 
 | ||||||
|  | 	minimum_size_changed.connect(_on_size_changed) | ||||||
|  | 	_on_size_changed.call_deferred() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _draw_port(slot_index: int, port_position: Vector2i, left: bool, color: Color) -> void: | ||||||
|  | 	if horizontal: | ||||||
|  | 		if is_slot_enabled_left(1): | ||||||
|  | 			draw_texture(PORT_LEFT_ICON, Vector2(0, size.y / 2) + Vector2(-4, -5), color) | ||||||
|  | 		if is_slot_enabled_right(1): | ||||||
|  | 			draw_texture(PORT_RIGHT_ICON, Vector2(size.x, size.y / 2) + Vector2(-5, -4.5), color) | ||||||
|  | 	else: | ||||||
|  | 		if slot_index == 0 and is_slot_enabled_left(0): | ||||||
|  | 			draw_texture(PORT_TOP_ICON, Vector2(size.x / 2, 0) + Vector2(-4.5, -7), color) | ||||||
|  | 		elif slot_index == 1: | ||||||
|  | 			draw_texture(PORT_BOTTOM_ICON, Vector2(size.x / 2, size.y) + Vector2(-4.5, -5), color) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_custom_input_port_position(horizontal: bool) -> Vector2: | ||||||
|  | 	if horizontal: | ||||||
|  | 		return Vector2(0, size.y / 2) | ||||||
|  | 	else: | ||||||
|  | 		return Vector2(size.x/2, 0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_custom_output_port_position(horizontal: bool) -> Vector2: | ||||||
|  | 	if horizontal: | ||||||
|  | 		return Vector2(size.x, size.y / 2) | ||||||
|  | 	else: | ||||||
|  | 		return Vector2(size.x / 2, size.y) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_status(status: int) -> void: | ||||||
|  | 	match status: | ||||||
|  | 		0: _set_stylebox_overrides(frames.panel_success, frames.titlebar_success) | ||||||
|  | 		1: _set_stylebox_overrides(frames.panel_failure, frames.titlebar_failure) | ||||||
|  | 		2: _set_stylebox_overrides(frames.panel_running, frames.titlebar_running) | ||||||
|  | 		_: _set_stylebox_overrides(frames.panel_normal, frames.titlebar_normal) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_slots(left_enabled: bool, right_enabled: bool) -> void: | ||||||
|  | 	if horizontal: | ||||||
|  | 		set_slot(1, left_enabled, -1, Color.WHITE, right_enabled, -1, Color.WHITE, PORT_LEFT_ICON, PORT_RIGHT_ICON) | ||||||
|  | 	else: | ||||||
|  | 		set_slot(0, left_enabled, -1, Color.WHITE, false, -1, Color.TRANSPARENT, PORT_TOP_ICON, null) | ||||||
|  | 		set_slot(2, false, -1, Color.TRANSPARENT, right_enabled, -1, Color.WHITE, null, PORT_BOTTOM_ICON) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_color(color: Color) -> void: | ||||||
|  | 	set_input_color(color) | ||||||
|  | 	set_output_color(color) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_input_color(color: Color) -> void: | ||||||
|  | 	set_slot_color_left(1 if horizontal else 0, color) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_output_color(color: Color) -> void: | ||||||
|  | 	set_slot_color_right(1 if horizontal else 2, color) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _set_stylebox_overrides(panel_stylebox: StyleBox, titlebar_stylebox: StyleBox) -> void: | ||||||
|  | 	add_theme_stylebox_override("panel", panel_stylebox) | ||||||
|  | 	add_theme_stylebox_override("titlebar", titlebar_stylebox) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_size_changed(): | ||||||
|  | 	add_theme_constant_override("port_offset", 12 * BeehaveUtils.get_editor_scale() if horizontal else round(size.x)) | ||||||
							
								
								
									
										47
									
								
								addons/beehave/debug/old_frames.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								addons/beehave/debug/old_frames.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | @tool | ||||||
|  | extends RefCounted | ||||||
|  | 
 | ||||||
|  | const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") | ||||||
|  | 
 | ||||||
|  | const SUCCESS_COLOR := Color("#009944c8") | ||||||
|  | const NORMAL_COLOR := Color("#15181e") | ||||||
|  | const FAILURE_COLOR := Color("#cf000f80") | ||||||
|  | const RUNNING_COLOR := Color("#ffcc00c8") | ||||||
|  | 
 | ||||||
|  | var empty: StyleBoxEmpty | ||||||
|  | var normal: StyleBoxFlat | ||||||
|  | var success: StyleBoxFlat | ||||||
|  | var failure: StyleBoxFlat | ||||||
|  | var running: StyleBoxFlat | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _init() -> void: | ||||||
|  | 	var plugin := BeehaveUtils.get_plugin() | ||||||
|  | 	if not plugin: | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	var editor_scale := BeehaveUtils.get_editor_scale() | ||||||
|  | 
 | ||||||
|  | 	empty = StyleBoxEmpty.new() | ||||||
|  | 
 | ||||||
|  | 	normal = ( | ||||||
|  | 		plugin | ||||||
|  | 		. get_editor_interface() | ||||||
|  | 		. get_base_control() | ||||||
|  | 		. get_theme_stylebox(&"frame", &"GraphNode") | ||||||
|  | 		. duplicate() | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	success = ( | ||||||
|  | 		plugin | ||||||
|  | 		. get_editor_interface() | ||||||
|  | 		. get_base_control() | ||||||
|  | 		. get_theme_stylebox(&"selected_frame", &"GraphNode") | ||||||
|  | 		. duplicate() | ||||||
|  | 	) | ||||||
|  | 	failure = success.duplicate() | ||||||
|  | 	running = success.duplicate() | ||||||
|  | 
 | ||||||
|  | 	success.border_color = SUCCESS_COLOR | ||||||
|  | 	failure.border_color = FAILURE_COLOR | ||||||
|  | 	running.border_color = RUNNING_COLOR | ||||||
							
								
								
									
										286
									
								
								addons/beehave/debug/old_graph_edit.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								addons/beehave/debug/old_graph_edit.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,286 @@ | |||||||
|  | @tool | ||||||
|  | extends GraphEdit | ||||||
|  | 
 | ||||||
|  | const BeehaveGraphNode := preload("old_graph_node.gd") | ||||||
|  | 
 | ||||||
|  | const HORIZONTAL_LAYOUT_ICON := preload("icons/horizontal_layout.svg") | ||||||
|  | const VERTICAL_LAYOUT_ICON := preload("icons/vertical_layout.svg") | ||||||
|  | 
 | ||||||
|  | const PROGRESS_SHIFT: int = 50 | ||||||
|  | const INACTIVE_COLOR: Color = Color("#898989aa") | ||||||
|  | const ACTIVE_COLOR: Color = Color("#ffcc00c8") | ||||||
|  | const SUCCESS_COLOR: Color = Color("#009944c8") | ||||||
|  | 
 | ||||||
|  | var updating_graph: bool = false | ||||||
|  | var arraging_nodes: bool = false | ||||||
|  | var beehave_tree: Dictionary: | ||||||
|  | 	set(value): | ||||||
|  | 		if beehave_tree == value: | ||||||
|  | 			return | ||||||
|  | 		beehave_tree = value | ||||||
|  | 		active_nodes.clear() | ||||||
|  | 		_update_graph() | ||||||
|  | 
 | ||||||
|  | var horizontal_layout: bool = false: | ||||||
|  | 	set(value): | ||||||
|  | 		if updating_graph or arraging_nodes: | ||||||
|  | 			return | ||||||
|  | 		if horizontal_layout == value: | ||||||
|  | 			return | ||||||
|  | 		horizontal_layout = value | ||||||
|  | 		_update_layout_button() | ||||||
|  | 		_update_graph() | ||||||
|  | 
 | ||||||
|  | var frames: RefCounted | ||||||
|  | var active_nodes: Array[String] | ||||||
|  | var progress: int = 0 | ||||||
|  | var layout_button: Button | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _init(frames: RefCounted) -> void: | ||||||
|  | 	self.frames = frames | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _ready() -> void: | ||||||
|  | 	custom_minimum_size = Vector2(100, 300) | ||||||
|  | 	set("arrange_nodes_button_hidden", true) | ||||||
|  | 	minimap_enabled = false | ||||||
|  | 	layout_button = Button.new() | ||||||
|  | 	layout_button.flat = true | ||||||
|  | 	layout_button.focus_mode = Control.FOCUS_NONE | ||||||
|  | 	layout_button.pressed.connect(func(): horizontal_layout = not horizontal_layout) | ||||||
|  | 	get_menu_container().add_child(layout_button) | ||||||
|  | 	_update_layout_button() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _update_graph() -> void: | ||||||
|  | 	if updating_graph: | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	updating_graph = true | ||||||
|  | 
 | ||||||
|  | 	clear_connections() | ||||||
|  | 
 | ||||||
|  | 	for child in _get_child_nodes(): | ||||||
|  | 		remove_child(child) | ||||||
|  | 		child.queue_free() | ||||||
|  | 
 | ||||||
|  | 	if not beehave_tree.is_empty(): | ||||||
|  | 		_add_nodes(beehave_tree) | ||||||
|  | 		_connect_nodes(beehave_tree) | ||||||
|  | 		_arrange_nodes.call_deferred(beehave_tree) | ||||||
|  | 
 | ||||||
|  | 	updating_graph = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _add_nodes(node: Dictionary) -> void: | ||||||
|  | 	if node.is_empty(): | ||||||
|  | 		return | ||||||
|  | 	var gnode := BeehaveGraphNode.new(frames, horizontal_layout) | ||||||
|  | 	add_child(gnode) | ||||||
|  | 	gnode.title_text = node.name | ||||||
|  | 	gnode.name = node.id | ||||||
|  | 	gnode.icon = _get_icon(node.type.back()) | ||||||
|  | 
 | ||||||
|  | 	if node.type.has(&"BeehaveTree"): | ||||||
|  | 		gnode.set_slots(false, true) | ||||||
|  | 	elif node.type.has(&"Leaf"): | ||||||
|  | 		gnode.set_slots(true, false) | ||||||
|  | 	elif node.type.has(&"Composite") or node.type.has(&"Decorator"): | ||||||
|  | 		gnode.set_slots(true, true) | ||||||
|  | 
 | ||||||
|  | 	for child in node.get("children", []): | ||||||
|  | 		_add_nodes(child) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _connect_nodes(node: Dictionary) -> void: | ||||||
|  | 	for child in node.get("children", []): | ||||||
|  | 		connect_node(node.id, 0, child.id, 0) | ||||||
|  | 		_connect_nodes(child) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _arrange_nodes(node: Dictionary) -> void: | ||||||
|  | 	if arraging_nodes: | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	arraging_nodes = true | ||||||
|  | 
 | ||||||
|  | 	var tree_node := _create_tree_nodes(node) | ||||||
|  | 	tree_node.update_positions(horizontal_layout) | ||||||
|  | 	_place_nodes(tree_node) | ||||||
|  | 
 | ||||||
|  | 	arraging_nodes = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _create_tree_nodes(node: Dictionary, root: TreeNode = null) -> TreeNode: | ||||||
|  | 	var tree_node := TreeNode.new(get_node(node.id), root) | ||||||
|  | 	for child in node.get("children", []): | ||||||
|  | 		var child_node := _create_tree_nodes(child, tree_node) | ||||||
|  | 		tree_node.children.push_back(child_node) | ||||||
|  | 	return tree_node | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _place_nodes(node: TreeNode) -> void: | ||||||
|  | 	node.item.position_offset = Vector2(node.x, node.y) | ||||||
|  | 	for child in node.children: | ||||||
|  | 		_place_nodes(child) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_icon(type: StringName) -> Texture2D: | ||||||
|  | 	var classes := ProjectSettings.get_global_class_list() | ||||||
|  | 	for c in classes: | ||||||
|  | 		if c["class"] == type: | ||||||
|  | 			var icon_path := c.get("icon", String()) | ||||||
|  | 			if not icon_path.is_empty(): | ||||||
|  | 				return load(icon_path) | ||||||
|  | 	return null | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_menu_container() -> Control: | ||||||
|  | 	return call("get_zoom_hbox") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_status(status: int) -> String: | ||||||
|  | 	if status == 0: | ||||||
|  | 		return "SUCCESS" | ||||||
|  | 	elif status == 1: | ||||||
|  | 		return "FAILURE" | ||||||
|  | 	return "RUNNING" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func process_begin(instance_id: int) -> void: | ||||||
|  | 	if not _is_same_tree(instance_id): | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	for child in _get_child_nodes(): | ||||||
|  | 		child.set_meta("status", -1) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func process_tick(instance_id: int, status: int) -> void: | ||||||
|  | 	var node := get_node_or_null(str(instance_id)) | ||||||
|  | 	if node: | ||||||
|  | 		node.text = "Status: %s" % get_status(status) | ||||||
|  | 		node.set_status(status) | ||||||
|  | 		node.set_meta("status", status) | ||||||
|  | 		if status == 0 or status == 2: | ||||||
|  | 			if not active_nodes.has(node.name): | ||||||
|  | 				active_nodes.push_back(node.name) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func process_end(instance_id: int) -> void: | ||||||
|  | 	if not _is_same_tree(instance_id): | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	for child in _get_child_nodes(): | ||||||
|  | 		var status := child.get_meta("status", -1) | ||||||
|  | 		match status: | ||||||
|  | 			0: | ||||||
|  | 				active_nodes.erase(child.name) | ||||||
|  | 				child.set_color(SUCCESS_COLOR) | ||||||
|  | 			1: | ||||||
|  | 				active_nodes.erase(child.name) | ||||||
|  | 				child.set_color(INACTIVE_COLOR) | ||||||
|  | 			2: | ||||||
|  | 				child.set_color(ACTIVE_COLOR) | ||||||
|  | 			_: | ||||||
|  | 				child.text = " " | ||||||
|  | 				child.set_status(status) | ||||||
|  | 				child.set_color(INACTIVE_COLOR) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _is_same_tree(instance_id: int) -> bool: | ||||||
|  | 	return str(instance_id) == beehave_tree.get("id", "") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_child_nodes() -> Array[Node]: | ||||||
|  | 	return get_children().filter(func(child): return child is BeehaveGraphNode) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_connection_line(from_position: Vector2, to_position: Vector2) -> PackedVector2Array: | ||||||
|  | 	var points: PackedVector2Array | ||||||
|  | 
 | ||||||
|  | 	from_position = from_position.round() | ||||||
|  | 	to_position = to_position.round() | ||||||
|  | 
 | ||||||
|  | 	points.push_back(from_position) | ||||||
|  | 
 | ||||||
|  | 	var mid_position := ((to_position + from_position) / 2).round() | ||||||
|  | 	if horizontal_layout: | ||||||
|  | 		points.push_back(Vector2(mid_position.x, from_position.y)) | ||||||
|  | 		points.push_back(Vector2(mid_position.x, to_position.y)) | ||||||
|  | 	else: | ||||||
|  | 		points.push_back(Vector2(from_position.x, mid_position.y)) | ||||||
|  | 		points.push_back(Vector2(to_position.x, mid_position.y)) | ||||||
|  | 
 | ||||||
|  | 	points.push_back(to_position) | ||||||
|  | 
 | ||||||
|  | 	return points | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _process(delta: float) -> void: | ||||||
|  | 	if not active_nodes.is_empty(): | ||||||
|  | 		progress += 10 if delta >= 0.05 else 1 | ||||||
|  | 		if progress >= 1000: | ||||||
|  | 			progress = 0 | ||||||
|  | 		queue_redraw() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _draw() -> void: | ||||||
|  | 	if active_nodes.is_empty(): | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	var circle_size: float = max(3, 6 * zoom) | ||||||
|  | 	var progress_shift: float = PROGRESS_SHIFT * zoom | ||||||
|  | 
 | ||||||
|  | 	var connections := get_connection_list() | ||||||
|  | 	for c in connections: | ||||||
|  | 		var from_node: StringName | ||||||
|  | 		var to_node: StringName | ||||||
|  | 
 | ||||||
|  | 		from_node = c.from | ||||||
|  | 		to_node = c.to | ||||||
|  | 
 | ||||||
|  | 		if not from_node in active_nodes or not c.to_node in active_nodes: | ||||||
|  | 			continue | ||||||
|  | 
 | ||||||
|  | 		var from := get_node(String(from_node)) | ||||||
|  | 		var to := get_node(String(to_node)) | ||||||
|  | 
 | ||||||
|  | 		if from.get_meta("status", -1) < 0 or to.get_meta("status", -1) < 0: | ||||||
|  | 			return | ||||||
|  | 
 | ||||||
|  | 		var output_port_position: Vector2 | ||||||
|  | 		var input_port_position: Vector2 | ||||||
|  | 
 | ||||||
|  | 		output_port_position = ( | ||||||
|  | 			from.position + from.call("get_connection_output_position", c.from_port) | ||||||
|  | 		) | ||||||
|  | 		input_port_position = to.position + to.call("get_connection_input_position", c.to_port) | ||||||
|  | 
 | ||||||
|  | 		var line := _get_connection_line(output_port_position, input_port_position) | ||||||
|  | 
 | ||||||
|  | 		var curve = Curve2D.new() | ||||||
|  | 		for l in line: | ||||||
|  | 			curve.add_point(l) | ||||||
|  | 
 | ||||||
|  | 		var max_steps := int(curve.get_baked_length()) | ||||||
|  | 		var current_shift := progress % max_steps | ||||||
|  | 		var p := curve.sample_baked(current_shift) | ||||||
|  | 		draw_circle(p, circle_size, ACTIVE_COLOR) | ||||||
|  | 
 | ||||||
|  | 		var shift := current_shift - progress_shift | ||||||
|  | 		while shift >= 0: | ||||||
|  | 			draw_circle(curve.sample_baked(shift), circle_size, ACTIVE_COLOR) | ||||||
|  | 			shift -= progress_shift | ||||||
|  | 
 | ||||||
|  | 		shift = current_shift + progress_shift | ||||||
|  | 		while shift <= curve.get_baked_length(): | ||||||
|  | 			draw_circle(curve.sample_baked(shift), circle_size, ACTIVE_COLOR) | ||||||
|  | 			shift += progress_shift | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _update_layout_button() -> void: | ||||||
|  | 	layout_button.icon = VERTICAL_LAYOUT_ICON if horizontal_layout else HORIZONTAL_LAYOUT_ICON | ||||||
|  | 	layout_button.tooltip_text = ( | ||||||
|  | 		"Switch to Vertical layout" if horizontal_layout else "Switch to Horizontal layout" | ||||||
|  | 	) | ||||||
							
								
								
									
										166
									
								
								addons/beehave/debug/old_graph_node.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								addons/beehave/debug/old_graph_node.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,166 @@ | |||||||
|  | @tool | ||||||
|  | extends GraphNode | ||||||
|  | 
 | ||||||
|  | const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") | ||||||
|  | 
 | ||||||
|  | const DEFAULT_COLOR := Color("#dad4cb") | ||||||
|  | 
 | ||||||
|  | const PORT_TOP_ICON := preload("icons/port_top.svg") | ||||||
|  | const PORT_BOTTOM_ICON := preload("icons/port_bottom.svg") | ||||||
|  | const PORT_LEFT_ICON := preload("icons/port_left.svg") | ||||||
|  | const PORT_RIGHT_ICON := preload("icons/port_right.svg") | ||||||
|  | 
 | ||||||
|  | @export var title_text: String: | ||||||
|  | 	set(value): | ||||||
|  | 		title_text = value | ||||||
|  | 		if title_label: | ||||||
|  | 			title_label.text = value | ||||||
|  | 
 | ||||||
|  | @export var text: String: | ||||||
|  | 	set(value): | ||||||
|  | 		text = value | ||||||
|  | 		if label: | ||||||
|  | 			label.text = " " if text.is_empty() else text | ||||||
|  | 
 | ||||||
|  | @export var icon: Texture2D: | ||||||
|  | 	set(value): | ||||||
|  | 		icon = value | ||||||
|  | 		if icon_rect: | ||||||
|  | 			icon_rect.texture = value | ||||||
|  | 
 | ||||||
|  | var layout_size: float: | ||||||
|  | 	get: | ||||||
|  | 		return size.y if horizontal else size.x | ||||||
|  | 
 | ||||||
|  | var panel: PanelContainer | ||||||
|  | var icon_rect: TextureRect | ||||||
|  | var title_label: Label | ||||||
|  | var container: VBoxContainer | ||||||
|  | var label: Label | ||||||
|  | 
 | ||||||
|  | var frames: RefCounted | ||||||
|  | var horizontal: bool = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _init(frames: RefCounted, horizontal: bool = false) -> void: | ||||||
|  | 	self.frames = frames | ||||||
|  | 	self.horizontal = horizontal | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _ready() -> void: | ||||||
|  | 	custom_minimum_size = Vector2(50, 50) * BeehaveUtils.get_editor_scale() | ||||||
|  | 	draggable = false | ||||||
|  | 
 | ||||||
|  | 	add_theme_stylebox_override("frame", frames.empty if frames != null else null) | ||||||
|  | 	add_theme_stylebox_override("selected_frame", frames.empty if frames != null else null) | ||||||
|  | 	add_theme_color_override("close_color", Color.TRANSPARENT) | ||||||
|  | 	add_theme_icon_override("close", ImageTexture.new()) | ||||||
|  | 
 | ||||||
|  | 	# For top port | ||||||
|  | 	add_child(Control.new()) | ||||||
|  | 
 | ||||||
|  | 	panel = PanelContainer.new() | ||||||
|  | 	panel.mouse_filter = Control.MOUSE_FILTER_PASS | ||||||
|  | 	panel.add_theme_stylebox_override("panel", frames.normal if frames != null else null) | ||||||
|  | 	add_child(panel) | ||||||
|  | 
 | ||||||
|  | 	var vbox_container := VBoxContainer.new() | ||||||
|  | 	panel.add_child(vbox_container) | ||||||
|  | 
 | ||||||
|  | 	var title_size := 24 * BeehaveUtils.get_editor_scale() | ||||||
|  | 	var margin_container := MarginContainer.new() | ||||||
|  | 	margin_container.add_theme_constant_override( | ||||||
|  | 		"margin_top", -title_size - 2 * BeehaveUtils.get_editor_scale() | ||||||
|  | 	) | ||||||
|  | 	margin_container.mouse_filter = Control.MOUSE_FILTER_PASS | ||||||
|  | 	vbox_container.add_child(margin_container) | ||||||
|  | 
 | ||||||
|  | 	var title_container := HBoxContainer.new() | ||||||
|  | 	title_container.add_child(Control.new()) | ||||||
|  | 	title_container.mouse_filter = Control.MOUSE_FILTER_PASS | ||||||
|  | 	title_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL | ||||||
|  | 	margin_container.add_child(title_container) | ||||||
|  | 
 | ||||||
|  | 	icon_rect = TextureRect.new() | ||||||
|  | 	icon_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED | ||||||
|  | 	title_container.add_child(icon_rect) | ||||||
|  | 
 | ||||||
|  | 	title_label = Label.new() | ||||||
|  | 	title_label.add_theme_color_override("font_color", DEFAULT_COLOR) | ||||||
|  | 	title_label.add_theme_font_override("font", get_theme_font("title_font")) | ||||||
|  | 	title_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER | ||||||
|  | 	title_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL | ||||||
|  | 	title_label.text = title_text | ||||||
|  | 	title_container.add_child(title_label) | ||||||
|  | 
 | ||||||
|  | 	title_container.add_child(Control.new()) | ||||||
|  | 
 | ||||||
|  | 	container = VBoxContainer.new() | ||||||
|  | 	container.size_flags_vertical = Control.SIZE_EXPAND_FILL | ||||||
|  | 	container.size_flags_horizontal = Control.SIZE_EXPAND_FILL | ||||||
|  | 	panel.add_child(container) | ||||||
|  | 
 | ||||||
|  | 	label = Label.new() | ||||||
|  | 	label.text = " " if text.is_empty() else text | ||||||
|  | 	container.add_child(label) | ||||||
|  | 
 | ||||||
|  | 	# For bottom port | ||||||
|  | 	add_child(Control.new()) | ||||||
|  | 
 | ||||||
|  | 	minimum_size_changed.connect(_on_size_changed) | ||||||
|  | 	_on_size_changed.call_deferred() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_status(status: int) -> void: | ||||||
|  | 	panel.add_theme_stylebox_override("panel", _get_stylebox(status)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_stylebox(status: int) -> StyleBox: | ||||||
|  | 	match status: | ||||||
|  | 		0: | ||||||
|  | 			return frames.success | ||||||
|  | 		1: | ||||||
|  | 			return frames.failure | ||||||
|  | 		2: | ||||||
|  | 			return frames.running | ||||||
|  | 		_: | ||||||
|  | 			return frames.normal | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_slots(left_enabled: bool, right_enabled: bool) -> void: | ||||||
|  | 	if horizontal: | ||||||
|  | 		set_slot( | ||||||
|  | 			1, | ||||||
|  | 			left_enabled, | ||||||
|  | 			0, | ||||||
|  | 			Color.WHITE, | ||||||
|  | 			right_enabled, | ||||||
|  | 			0, | ||||||
|  | 			Color.WHITE, | ||||||
|  | 			PORT_LEFT_ICON, | ||||||
|  | 			PORT_RIGHT_ICON | ||||||
|  | 		) | ||||||
|  | 	else: | ||||||
|  | 		set_slot(0, left_enabled, 0, Color.WHITE, false, -2, Color.TRANSPARENT, PORT_TOP_ICON, null) | ||||||
|  | 		set_slot( | ||||||
|  | 			2, false, -1, Color.TRANSPARENT, right_enabled, 0, Color.WHITE, null, PORT_BOTTOM_ICON | ||||||
|  | 		) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_color(color: Color) -> void: | ||||||
|  | 	set_input_color(color) | ||||||
|  | 	set_output_color(color) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_input_color(color: Color) -> void: | ||||||
|  | 	set_slot_color_left(1 if horizontal else 0, color) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func set_output_color(color: Color) -> void: | ||||||
|  | 	set_slot_color_right(1 if horizontal else 2, color) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_size_changed(): | ||||||
|  | 	add_theme_constant_override( | ||||||
|  | 		"port_offset", 12 * BeehaveUtils.get_editor_scale() if horizontal else round(size.x / 2.0) | ||||||
|  | 	) | ||||||
							
								
								
									
										275
									
								
								addons/beehave/debug/tree_node.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								addons/beehave/debug/tree_node.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,275 @@ | |||||||
|  | class_name TreeNode | ||||||
|  | extends RefCounted | ||||||
|  | 
 | ||||||
|  | # Based on https://rachel53461.wordpress.com/2014/04/20/algorithm-for-drawing-trees/ | ||||||
|  | 
 | ||||||
|  | const SIBLING_DISTANCE: float = 20.0 | ||||||
|  | const LEVEL_DISTANCE: float = 40.0 | ||||||
|  | 
 | ||||||
|  | const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") | ||||||
|  | 
 | ||||||
|  | var x: float | ||||||
|  | var y: float | ||||||
|  | var mod: float | ||||||
|  | var parent: TreeNode | ||||||
|  | var children: Array[TreeNode] | ||||||
|  | 
 | ||||||
|  | var item: GraphNode | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _init(p_item: GraphNode = null, p_parent: TreeNode = null) -> void: | ||||||
|  | 	parent = p_parent | ||||||
|  | 	item = p_item | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func is_leaf() -> bool: | ||||||
|  | 	return children.is_empty() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func is_most_left() -> bool: | ||||||
|  | 	if not parent: | ||||||
|  | 		return true | ||||||
|  | 	return parent.children.front() == self | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func is_most_right() -> bool: | ||||||
|  | 	if not parent: | ||||||
|  | 		return true | ||||||
|  | 	return parent.children.back() == self | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_previous_sibling() -> TreeNode: | ||||||
|  | 	if not parent or is_most_left(): | ||||||
|  | 		return null | ||||||
|  | 	return parent.children[parent.children.find(self) - 1] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_next_sibling() -> TreeNode: | ||||||
|  | 	if not parent or is_most_right(): | ||||||
|  | 		return null | ||||||
|  | 	return parent.children[parent.children.find(self) + 1] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_most_left_sibling() -> TreeNode: | ||||||
|  | 	if not parent: | ||||||
|  | 		return null | ||||||
|  | 
 | ||||||
|  | 	if is_most_left(): | ||||||
|  | 		return self | ||||||
|  | 
 | ||||||
|  | 	return parent.children.front() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_most_left_child() -> TreeNode: | ||||||
|  | 	if children.is_empty(): | ||||||
|  | 		return null | ||||||
|  | 	return children.front() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_most_right_child() -> TreeNode: | ||||||
|  | 	if children.is_empty(): | ||||||
|  | 		return null | ||||||
|  | 	return children.back() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func update_positions(horizontally: bool = false) -> void: | ||||||
|  | 	_initialize_nodes(self, 0) | ||||||
|  | 	_calculate_initial_x(self) | ||||||
|  | 
 | ||||||
|  | 	_check_all_children_on_screen(self) | ||||||
|  | 	_calculate_final_positions(self, 0) | ||||||
|  | 
 | ||||||
|  | 	if horizontally: | ||||||
|  | 		_swap_x_y(self) | ||||||
|  | 		_calculate_x(self, 0) | ||||||
|  | 	else: | ||||||
|  | 		_calculate_y(self, 0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _initialize_nodes(node: TreeNode, depth: int) -> void: | ||||||
|  | 	node.x = -1 | ||||||
|  | 	node.y = depth | ||||||
|  | 	node.mod = 0 | ||||||
|  | 
 | ||||||
|  | 	for child in node.children: | ||||||
|  | 		_initialize_nodes(child, depth + 1) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _calculate_initial_x(node: TreeNode) -> void: | ||||||
|  | 	for child in node.children: | ||||||
|  | 		_calculate_initial_x(child) | ||||||
|  | 	if node.is_leaf(): | ||||||
|  | 		if not node.is_most_left(): | ||||||
|  | 			node.x = ( | ||||||
|  | 				node.get_previous_sibling().x | ||||||
|  | 				+ node.get_previous_sibling().item.layout_size | ||||||
|  | 				+ SIBLING_DISTANCE | ||||||
|  | 			) | ||||||
|  | 		else: | ||||||
|  | 			node.x = 0 | ||||||
|  | 	else: | ||||||
|  | 		var mid: float | ||||||
|  | 		if node.children.size() == 1: | ||||||
|  | 			var offset: float = (node.children.front().item.layout_size - node.item.layout_size) / 2 | ||||||
|  | 			mid = node.children.front().x + offset | ||||||
|  | 		else: | ||||||
|  | 			var left_child := node.get_most_left_child() | ||||||
|  | 			var right_child := node.get_most_right_child() | ||||||
|  | 			mid = ( | ||||||
|  | 				( | ||||||
|  | 					left_child.x | ||||||
|  | 					+ right_child.x | ||||||
|  | 					+ right_child.item.layout_size | ||||||
|  | 					- node.item.layout_size | ||||||
|  | 				) | ||||||
|  | 				/ 2 | ||||||
|  | 			) | ||||||
|  | 
 | ||||||
|  | 		if node.is_most_left(): | ||||||
|  | 			node.x = mid | ||||||
|  | 		else: | ||||||
|  | 			node.x = ( | ||||||
|  | 				node.get_previous_sibling().x | ||||||
|  | 				+ node.get_previous_sibling().item.layout_size | ||||||
|  | 				+ SIBLING_DISTANCE | ||||||
|  | 			) | ||||||
|  | 			node.mod = node.x - mid | ||||||
|  | 
 | ||||||
|  | 	if not node.is_leaf() and not node.is_most_left(): | ||||||
|  | 		_check_for_conflicts(node) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _calculate_final_positions(node: TreeNode, mod_sum: float) -> void: | ||||||
|  | 	node.x += mod_sum | ||||||
|  | 	mod_sum += node.mod | ||||||
|  | 
 | ||||||
|  | 	for child in node.children: | ||||||
|  | 		_calculate_final_positions(child, mod_sum) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _check_all_children_on_screen(node: TreeNode) -> void: | ||||||
|  | 	var node_contour: Dictionary = {} | ||||||
|  | 	_get_left_contour(node, 0, node_contour) | ||||||
|  | 
 | ||||||
|  | 	var shift_amount: float = 0 | ||||||
|  | 	for y in node_contour.keys(): | ||||||
|  | 		if node_contour[y] + shift_amount < 0: | ||||||
|  | 			shift_amount = (node_contour[y] * -1) | ||||||
|  | 
 | ||||||
|  | 	if shift_amount > 0: | ||||||
|  | 		node.x += shift_amount | ||||||
|  | 		node.mod += shift_amount | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _check_for_conflicts(node: TreeNode) -> void: | ||||||
|  | 	var min_distance := SIBLING_DISTANCE | ||||||
|  | 	var shift_value: float = 0 | ||||||
|  | 	var shift_sibling: TreeNode = null | ||||||
|  | 
 | ||||||
|  | 	var node_contour: Dictionary = {}  # { int, float } | ||||||
|  | 	_get_left_contour(node, 0, node_contour) | ||||||
|  | 
 | ||||||
|  | 	var sibling := node.get_most_left_sibling() | ||||||
|  | 	while sibling != null and sibling != node: | ||||||
|  | 		var sibling_contour: Dictionary = {} | ||||||
|  | 		_get_right_contour(sibling, 0, sibling_contour) | ||||||
|  | 
 | ||||||
|  | 		for level in range( | ||||||
|  | 			node.y + 1, min(sibling_contour.keys().max(), node_contour.keys().max()) + 1 | ||||||
|  | 		): | ||||||
|  | 			var distance: float = node_contour[level] - sibling_contour[level] | ||||||
|  | 			if distance + shift_value < min_distance: | ||||||
|  | 				shift_value = min_distance - distance | ||||||
|  | 				shift_sibling = sibling | ||||||
|  | 
 | ||||||
|  | 		sibling = sibling.get_next_sibling() | ||||||
|  | 
 | ||||||
|  | 	if shift_value > 0: | ||||||
|  | 		node.x += shift_value | ||||||
|  | 		node.mod += shift_value | ||||||
|  | 		_center_nodes_between(shift_sibling, node) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _center_nodes_between(left_node: TreeNode, right_node: TreeNode) -> void: | ||||||
|  | 	var left_index := left_node.parent.children.find(left_node) | ||||||
|  | 	var right_index := left_node.parent.children.find(right_node) | ||||||
|  | 
 | ||||||
|  | 	var num_nodes_between: int = (right_index - left_index) - 1 | ||||||
|  | 	if num_nodes_between > 0: | ||||||
|  | 		# The extra distance that needs to be split into num_nodes_between + 1 | ||||||
|  | 		# in order to find the new node spacing so that nodes are equally spaced | ||||||
|  | 		var distance_to_allocate: float = right_node.x - left_node.x - left_node.item.layout_size | ||||||
|  | 		# Subtract sizes on nodes in between | ||||||
|  | 		for i in range(left_index + 1, right_index): | ||||||
|  | 			distance_to_allocate -= left_node.parent.children[i].item.layout_size | ||||||
|  | 		# Divide space equally | ||||||
|  | 		var distance_between_nodes: float = distance_to_allocate / (num_nodes_between + 1) | ||||||
|  | 
 | ||||||
|  | 		var prev_node := left_node | ||||||
|  | 		var middle_node := left_node.get_next_sibling() | ||||||
|  | 		while middle_node != right_node: | ||||||
|  | 			var desire_x: float = prev_node.x + prev_node.item.layout_size + distance_between_nodes | ||||||
|  | 			var offset := desire_x - middle_node.x | ||||||
|  | 			middle_node.x += offset | ||||||
|  | 			middle_node.mod += offset | ||||||
|  | 			prev_node = middle_node | ||||||
|  | 			middle_node = middle_node.get_next_sibling() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_left_contour(node: TreeNode, mod_sum: float, values: Dictionary) -> void: | ||||||
|  | 	var node_left: float = node.x + mod_sum | ||||||
|  | 	var depth := int(node.y) | ||||||
|  | 	if not values.has(depth): | ||||||
|  | 		values[depth] = node_left | ||||||
|  | 	else: | ||||||
|  | 		values[depth] = min(values[depth], node_left) | ||||||
|  | 
 | ||||||
|  | 	mod_sum += node.mod | ||||||
|  | 	for child in node.children: | ||||||
|  | 		_get_left_contour(child, mod_sum, values) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_right_contour(node: TreeNode, mod_sum: float, values: Dictionary) -> void: | ||||||
|  | 	var node_right: float = node.x + mod_sum + node.item.layout_size | ||||||
|  | 	var depth := int(node.y) | ||||||
|  | 	if not values.has(depth): | ||||||
|  | 		values[depth] = node_right | ||||||
|  | 	else: | ||||||
|  | 		values[depth] = max(values[depth], node_right) | ||||||
|  | 
 | ||||||
|  | 	mod_sum += node.mod | ||||||
|  | 	for child in node.children: | ||||||
|  | 		_get_right_contour(child, mod_sum, values) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _swap_x_y(node: TreeNode) -> void: | ||||||
|  | 	for child in node.children: | ||||||
|  | 		_swap_x_y(child) | ||||||
|  | 
 | ||||||
|  | 	var temp := node.x | ||||||
|  | 	node.x = node.y | ||||||
|  | 	node.y = temp | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _calculate_x(node: TreeNode, offset: int) -> void: | ||||||
|  | 	node.x = offset | ||||||
|  | 	var sibling := node.get_most_left_sibling() | ||||||
|  | 	var max_size: int = node.item.size.x | ||||||
|  | 	while sibling != null: | ||||||
|  | 		max_size = max(sibling.item.size.x, max_size) | ||||||
|  | 		sibling = sibling.get_next_sibling() | ||||||
|  | 
 | ||||||
|  | 	for child in node.children: | ||||||
|  | 		_calculate_x(child, max_size + offset + LEVEL_DISTANCE * BeehaveUtils.get_editor_scale()) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _calculate_y(node: TreeNode, offset: int) -> void: | ||||||
|  | 	node.y = offset | ||||||
|  | 	var sibling := node.get_most_left_sibling() | ||||||
|  | 	var max_size: int = node.item.size.y | ||||||
|  | 	while sibling != null: | ||||||
|  | 		max_size = max(sibling.item.size.y, max_size) | ||||||
|  | 		sibling = sibling.get_next_sibling() | ||||||
|  | 
 | ||||||
|  | 	for child in node.children: | ||||||
|  | 		_calculate_y(child, max_size + offset + LEVEL_DISTANCE * BeehaveUtils.get_editor_scale()) | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/action.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/action.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/action.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/action.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://btrq8e0kyxthg" | ||||||
|  | path="res://.godot/imported/action.svg-e8a91246d0ba9ba3cf84290d65648f06.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/action.svg" | ||||||
|  | dest_files=["res://.godot/imported/action.svg-e8a91246d0ba9ba3cf84290d65648f06.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/blackboard.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/blackboard.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/blackboard.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/blackboard.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://dw7rom0hiff6c" | ||||||
|  | path="res://.godot/imported/blackboard.svg-18d4dfd4f6de558de250b67251ff1e69.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/blackboard.svg" | ||||||
|  | dest_files=["res://.godot/imported/blackboard.svg-18d4dfd4f6de558de250b67251ff1e69.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/category_bt.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/category_bt.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/category_bt.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/category_bt.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://qpdd6ue7x82h" | ||||||
|  | path="res://.godot/imported/category_bt.svg-8537bebd1c5f62dca3d7ee7f17efeed4.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/category_bt.svg" | ||||||
|  | dest_files=["res://.godot/imported/category_bt.svg-8537bebd1c5f62dca3d7ee7f17efeed4.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/category_composite.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/category_composite.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/category_composite.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/category_composite.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://863s568sneja" | ||||||
|  | path="res://.godot/imported/category_composite.svg-43f66e63a7ccfa5ac8ec6da0583b3246.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/category_composite.svg" | ||||||
|  | dest_files=["res://.godot/imported/category_composite.svg-43f66e63a7ccfa5ac8ec6da0583b3246.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/category_decorator.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/category_decorator.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/category_decorator.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/category_decorator.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://c2ie8m4ddawlb" | ||||||
|  | path="res://.godot/imported/category_decorator.svg-79d598d6456f32724156248e09d6eaf3.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/category_decorator.svg" | ||||||
|  | dest_files=["res://.godot/imported/category_decorator.svg-79d598d6456f32724156248e09d6eaf3.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/category_leaf.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/category_leaf.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/category_leaf.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/category_leaf.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://eq0sp4g3s75r" | ||||||
|  | path="res://.godot/imported/category_leaf.svg-c740ecab6cfae632574ca5e39e46fd2e.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/category_leaf.svg" | ||||||
|  | dest_files=["res://.godot/imported/category_leaf.svg-c740ecab6cfae632574ca5e39e46fd2e.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/condition.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/condition.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/condition.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/condition.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://ck4toqx0nggiu" | ||||||
|  | path="res://.godot/imported/condition.svg-57892684b10a64086f68c09c388b17e5.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/condition.svg" | ||||||
|  | dest_files=["res://.godot/imported/condition.svg-57892684b10a64086f68c09c388b17e5.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/cooldown.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/cooldown.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										37
									
								
								addons/beehave/icons/cooldown.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								addons/beehave/icons/cooldown.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://cb1g6e3mmj40d" | ||||||
|  | path="res://.godot/imported/cooldown.svg-2fb8975b5974e35bedad825abb9faf66.ctex" | ||||||
|  | metadata={ | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/cooldown.svg" | ||||||
|  | dest_files=["res://.godot/imported/cooldown.svg-2fb8975b5974e35bedad825abb9faf66.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=false | ||||||
|  | editor/convert_colors_with_editor_theme=false | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/delayer.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/delayer.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										37
									
								
								addons/beehave/icons/delayer.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								addons/beehave/icons/delayer.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://btc5ynpdytj7j" | ||||||
|  | path="res://.godot/imported/delayer.svg-6f92c97f61b1eb8679428f438e6b08c7.ctex" | ||||||
|  | metadata={ | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/delayer.svg" | ||||||
|  | dest_files=["res://.godot/imported/delayer.svg-6f92c97f61b1eb8679428f438e6b08c7.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=false | ||||||
|  | editor/convert_colors_with_editor_theme=false | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/failer.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/failer.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/failer.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/failer.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://2fj7htaqvcud" | ||||||
|  | path="res://.godot/imported/failer.svg-9a62b840e1eacc0437e7a67b14a302e4.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/failer.svg" | ||||||
|  | dest_files=["res://.godot/imported/failer.svg-9a62b840e1eacc0437e7a67b14a302e4.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/inverter.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/inverter.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/inverter.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/inverter.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://cffmoc3og8hux" | ||||||
|  | path="res://.godot/imported/inverter.svg-1f1b976d95de42c4ad99a92fa9a6c5d0.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/inverter.svg" | ||||||
|  | dest_files=["res://.godot/imported/inverter.svg-1f1b976d95de42c4ad99a92fa9a6c5d0.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/limiter.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/limiter.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/limiter.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/limiter.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://c7akxvsg0f2by" | ||||||
|  | path="res://.godot/imported/limiter.svg-b4c7646605c46f53c5e403fe21d8f584.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/limiter.svg" | ||||||
|  | dest_files=["res://.godot/imported/limiter.svg-b4c7646605c46f53c5e403fe21d8f584.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/repeater.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/repeater.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										37
									
								
								addons/beehave/icons/repeater.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								addons/beehave/icons/repeater.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://cxxmf535lln2p" | ||||||
|  | path="res://.godot/imported/repeater.svg-be2d3a7f1a46d7ba1d1939553725f598.ctex" | ||||||
|  | metadata={ | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/repeater.svg" | ||||||
|  | dest_files=["res://.godot/imported/repeater.svg-be2d3a7f1a46d7ba1d1939553725f598.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=false | ||||||
|  | editor/convert_colors_with_editor_theme=false | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/selector.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/selector.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/selector.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/selector.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://b2c5d20doh4sp" | ||||||
|  | path="res://.godot/imported/selector.svg-78bccfc448bd1676b5a29bfde4b08e5b.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/selector.svg" | ||||||
|  | dest_files=["res://.godot/imported/selector.svg-78bccfc448bd1676b5a29bfde4b08e5b.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/selector_random.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/selector_random.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/selector_random.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/selector_random.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://bmnkcmk7bkdjd" | ||||||
|  | path="res://.godot/imported/selector_random.svg-d52fea1352c24483ecd9dc8609cf00f3.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/selector_random.svg" | ||||||
|  | dest_files=["res://.godot/imported/selector_random.svg-d52fea1352c24483ecd9dc8609cf00f3.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/selector_reactive.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/selector_reactive.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/selector_reactive.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/selector_reactive.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://crkbov0h8sb8l" | ||||||
|  | path="res://.godot/imported/selector_reactive.svg-dd3b8fb8cd2ffe331605aaad1e021cc0.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/selector_reactive.svg" | ||||||
|  | dest_files=["res://.godot/imported/selector_reactive.svg-dd3b8fb8cd2ffe331605aaad1e021cc0.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/sequence.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/sequence.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/sequence.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/sequence.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://c5gw354thiofm" | ||||||
|  | path="res://.godot/imported/sequence.svg-76e5600611900cc81e9ec286977b8c6a.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/sequence.svg" | ||||||
|  | dest_files=["res://.godot/imported/sequence.svg-76e5600611900cc81e9ec286977b8c6a.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/sequence_random.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/sequence_random.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/sequence_random.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/sequence_random.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://bat8ptdw5qt1d" | ||||||
|  | path="res://.godot/imported/sequence_random.svg-58cee9098c622ef87db941279206422a.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/sequence_random.svg" | ||||||
|  | dest_files=["res://.godot/imported/sequence_random.svg-58cee9098c622ef87db941279206422a.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/sequence_reactive.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/sequence_reactive.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/sequence_reactive.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/sequence_reactive.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://rmiu1slwfkh7" | ||||||
|  | path="res://.godot/imported/sequence_reactive.svg-7d384ca290f7934adb9e17d9e7116b6c.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/sequence_reactive.svg" | ||||||
|  | dest_files=["res://.godot/imported/sequence_reactive.svg-7d384ca290f7934adb9e17d9e7116b6c.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/simple_parallel.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/simple_parallel.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										37
									
								
								addons/beehave/icons/simple_parallel.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								addons/beehave/icons/simple_parallel.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://bvinjswsagdbc" | ||||||
|  | path="res://.godot/imported/simple_parallel.svg-3d4107eaf2e46557f6d3be3249f91430.ctex" | ||||||
|  | metadata={ | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/simple_parallel.svg" | ||||||
|  | dest_files=["res://.godot/imported/simple_parallel.svg-3d4107eaf2e46557f6d3be3249f91430.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=false | ||||||
|  | editor/convert_colors_with_editor_theme=false | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/succeeder.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/succeeder.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/succeeder.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/succeeder.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://dl6wo332kglbe" | ||||||
|  | path="res://.godot/imported/succeeder.svg-e5cf6f6e04b9b862b82fd2cb479272aa.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/succeeder.svg" | ||||||
|  | dest_files=["res://.godot/imported/succeeder.svg-e5cf6f6e04b9b862b82fd2cb479272aa.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/tree.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/tree.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								addons/beehave/icons/tree.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								addons/beehave/icons/tree.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://deryyg2hbmaaw" | ||||||
|  | path="res://.godot/imported/tree.svg-c0b20ed88b2fe300c0296f7236049076.ctex" | ||||||
|  | metadata={ | ||||||
|  | "has_editor_variant": true, | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/tree.svg" | ||||||
|  | dest_files=["res://.godot/imported/tree.svg-c0b20ed88b2fe300c0296f7236049076.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=true | ||||||
|  | editor/convert_colors_with_editor_theme=true | ||||||
							
								
								
									
										
											BIN
										
									
								
								addons/beehave/icons/until_fail.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								addons/beehave/icons/until_fail.svg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										37
									
								
								addons/beehave/icons/until_fail.svg.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								addons/beehave/icons/until_fail.svg.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://b5b63h80o8din" | ||||||
|  | path="res://.godot/imported/until_fail.svg-8015014c40e91d9c2668ec34d4118b8e.ctex" | ||||||
|  | metadata={ | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://addons/beehave/icons/until_fail.svg" | ||||||
|  | dest_files=["res://.godot/imported/until_fail.svg-8015014c40e91d9c2668ec34d4118b8e.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | compress/hdr_compression=1 | ||||||
|  | compress/normal_map=0 | ||||||
|  | compress/channel_pack=0 | ||||||
|  | mipmaps/generate=false | ||||||
|  | mipmaps/limit=-1 | ||||||
|  | roughness/mode=0 | ||||||
|  | roughness/src_normal="" | ||||||
|  | process/fix_alpha_border=true | ||||||
|  | process/premult_alpha=false | ||||||
|  | process/normal_map_invert_y=false | ||||||
|  | process/hdr_as_srgb=false | ||||||
|  | process/hdr_clamp_exposure=false | ||||||
|  | process/size_limit=0 | ||||||
|  | detect_3d/compress_to=1 | ||||||
|  | svg/scale=1.0 | ||||||
|  | editor/scale_with_editor_scale=false | ||||||
|  | editor/convert_colors_with_editor_theme=false | ||||||
							
								
								
									
										54
									
								
								addons/beehave/metrics/beehave_global_metrics.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								addons/beehave/metrics/beehave_global_metrics.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | extends Node | ||||||
|  | 
 | ||||||
|  | var _tree_count: int = 0 | ||||||
|  | var _active_tree_count: int = 0 | ||||||
|  | var _registered_trees: Array = [] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _enter_tree() -> void: | ||||||
|  | 	Performance.add_custom_monitor("beehave/total_trees", _get_total_trees) | ||||||
|  | 	Performance.add_custom_monitor("beehave/total_enabled_trees", _get_total_enabled_trees) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func register_tree(tree) -> void: | ||||||
|  | 	if _registered_trees.has(tree): | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	_registered_trees.append(tree) | ||||||
|  | 	_tree_count += 1 | ||||||
|  | 
 | ||||||
|  | 	if tree.enabled: | ||||||
|  | 		_active_tree_count += 1 | ||||||
|  | 
 | ||||||
|  | 	tree.tree_enabled.connect(_on_tree_enabled) | ||||||
|  | 	tree.tree_disabled.connect(_on_tree_disabled) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func unregister_tree(tree) -> void: | ||||||
|  | 	if not _registered_trees.has(tree): | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	_registered_trees.erase(tree) | ||||||
|  | 	_tree_count -= 1 | ||||||
|  | 
 | ||||||
|  | 	if tree.enabled: | ||||||
|  | 		_active_tree_count -= 1 | ||||||
|  | 
 | ||||||
|  | 	tree.tree_enabled.disconnect(_on_tree_enabled) | ||||||
|  | 	tree.tree_disabled.disconnect(_on_tree_disabled) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_total_trees() -> int: | ||||||
|  | 	return _tree_count | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_total_enabled_trees() -> int: | ||||||
|  | 	return _active_tree_count | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_tree_enabled() -> void: | ||||||
|  | 	_active_tree_count += 1 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_tree_disabled() -> void: | ||||||
|  | 	_active_tree_count -= 1 | ||||||
							
								
								
									
										46
									
								
								addons/beehave/nodes/beehave_node.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								addons/beehave/nodes/beehave_node.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | @tool | ||||||
|  | class_name BeehaveNode extends Node | ||||||
|  | 
 | ||||||
|  | ## A node in the behavior tree. Every node must return `SUCCESS`, `FAILURE` or | ||||||
|  | ## `RUNNING` when ticked. | ||||||
|  | 
 | ||||||
|  | enum { SUCCESS, FAILURE, RUNNING } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_configuration_warnings() -> PackedStringArray: | ||||||
|  | 	var warnings: PackedStringArray = [] | ||||||
|  | 
 | ||||||
|  | 	if get_children().any(func(x): return not (x is BeehaveNode)): | ||||||
|  | 		warnings.append("All children of this node should inherit from BeehaveNode class.") | ||||||
|  | 
 | ||||||
|  | 	return warnings | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Executes this node and returns a status code. | ||||||
|  | ## This method must be overwritten. | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	return SUCCESS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Called when this node needs to be interrupted before it can return FAILURE or SUCCESS. | ||||||
|  | func interrupt(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Called before the first time it ticks by the parent. | ||||||
|  | func before_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Called after the last time it ticks and returns | ||||||
|  | ## [code]SUCCESS[/code] or [code]FAILURE[/code]. | ||||||
|  | func after_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	return [&"BeehaveNode"] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func can_send_message(blackboard: Blackboard) -> bool: | ||||||
|  | 	return blackboard.get_value("can_send_message", false) | ||||||
							
								
								
									
										329
									
								
								addons/beehave/nodes/beehave_tree.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										329
									
								
								addons/beehave/nodes/beehave_tree.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,329 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../icons/tree.svg") | ||||||
|  | class_name BeehaveTree extends Node | ||||||
|  | 
 | ||||||
|  | ## Controls the flow of execution of the entire behavior tree. | ||||||
|  | 
 | ||||||
|  | enum { SUCCESS, FAILURE, RUNNING } | ||||||
|  | 
 | ||||||
|  | enum ProcessThread { IDLE, PHYSICS } | ||||||
|  | 
 | ||||||
|  | signal tree_enabled | ||||||
|  | signal tree_disabled | ||||||
|  | 
 | ||||||
|  | ## Whether this behavior tree should be enabled or not. | ||||||
|  | @export var enabled: bool = true: | ||||||
|  | 	set(value): | ||||||
|  | 		enabled = value | ||||||
|  | 		set_physics_process(enabled and process_thread == ProcessThread.PHYSICS) | ||||||
|  | 		set_process(enabled and process_thread == ProcessThread.IDLE) | ||||||
|  | 		if value: | ||||||
|  | 			tree_enabled.emit() | ||||||
|  | 		else: | ||||||
|  | 			interrupt() | ||||||
|  | 			tree_disabled.emit() | ||||||
|  | 
 | ||||||
|  | 	get: | ||||||
|  | 		return enabled | ||||||
|  | 
 | ||||||
|  | ## How often the tree should tick, in frames. The default value of 1 means | ||||||
|  | ## tick() runs every frame. | ||||||
|  | @export var tick_rate: int = 1 | ||||||
|  | 
 | ||||||
|  | ## An optional node path this behavior tree should apply to. | ||||||
|  | @export_node_path var actor_node_path: NodePath: | ||||||
|  | 	set(anp): | ||||||
|  | 		actor_node_path = anp | ||||||
|  | 		if actor_node_path != null and str(actor_node_path) != "..": | ||||||
|  | 			actor = get_node(actor_node_path) | ||||||
|  | 		else: | ||||||
|  | 			actor = get_parent() | ||||||
|  | 		if Engine.is_editor_hint(): | ||||||
|  | 			update_configuration_warnings() | ||||||
|  | 
 | ||||||
|  | ## Whether to run this tree in a physics or idle thread. | ||||||
|  | @export var process_thread: ProcessThread = ProcessThread.PHYSICS: | ||||||
|  | 	set(value): | ||||||
|  | 		process_thread = value | ||||||
|  | 		set_physics_process(enabled and process_thread == ProcessThread.PHYSICS) | ||||||
|  | 		set_process(enabled and process_thread == ProcessThread.IDLE) | ||||||
|  | 
 | ||||||
|  | ## Custom blackboard node. An internal blackboard will be used | ||||||
|  | ## if no blackboard is provided explicitly. | ||||||
|  | @export var blackboard: Blackboard: | ||||||
|  | 	set(b): | ||||||
|  | 		blackboard = b | ||||||
|  | 		if blackboard and _internal_blackboard: | ||||||
|  | 			remove_child(_internal_blackboard) | ||||||
|  | 			_internal_blackboard.free() | ||||||
|  | 			_internal_blackboard = null | ||||||
|  | 		elif not blackboard and not _internal_blackboard: | ||||||
|  | 			_internal_blackboard = Blackboard.new() | ||||||
|  | 			add_child(_internal_blackboard, false, Node.INTERNAL_MODE_BACK) | ||||||
|  | 	get: | ||||||
|  | 		# in case blackboard is accessed before this node is, | ||||||
|  | 		# we need to ensure that the internal blackboard is used. | ||||||
|  | 		if not blackboard and not _internal_blackboard: | ||||||
|  | 			_internal_blackboard = Blackboard.new() | ||||||
|  | 			add_child(_internal_blackboard, false, Node.INTERNAL_MODE_BACK) | ||||||
|  | 		return blackboard if blackboard else _internal_blackboard | ||||||
|  | 
 | ||||||
|  | ## When enabled, this tree is tracked individually | ||||||
|  | ## as a custom monitor. | ||||||
|  | @export var custom_monitor = false: | ||||||
|  | 	set(b): | ||||||
|  | 		custom_monitor = b | ||||||
|  | 		if custom_monitor and _process_time_metric_name != "": | ||||||
|  | 			Performance.add_custom_monitor( | ||||||
|  | 				_process_time_metric_name, _get_process_time_metric_value | ||||||
|  | 			) | ||||||
|  | 			_get_global_metrics().register_tree(self) | ||||||
|  | 		else: | ||||||
|  | 			if _process_time_metric_name != "": | ||||||
|  | 				# Remove tree metric from the engine | ||||||
|  | 				Performance.remove_custom_monitor(_process_time_metric_name) | ||||||
|  | 				_get_global_metrics().unregister_tree(self) | ||||||
|  | 
 | ||||||
|  | 			BeehaveDebuggerMessages.unregister_tree(get_instance_id()) | ||||||
|  | 
 | ||||||
|  | @export var actor: Node: | ||||||
|  | 	set(a): | ||||||
|  | 		actor = a | ||||||
|  | 		if actor == null: | ||||||
|  | 			actor = get_parent() | ||||||
|  | 		if Engine.is_editor_hint(): | ||||||
|  | 			update_configuration_warnings() | ||||||
|  | 
 | ||||||
|  | var status: int = -1 | ||||||
|  | var last_tick: int = 0 | ||||||
|  | 
 | ||||||
|  | var _internal_blackboard: Blackboard | ||||||
|  | var _process_time_metric_name: String | ||||||
|  | var _process_time_metric_value: float = 0.0 | ||||||
|  | var _can_send_message: bool = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _ready() -> void: | ||||||
|  | 	var connect_scene_tree_signal = func(signal_name: String, is_added: bool): | ||||||
|  | 		if not get_tree().is_connected(signal_name, _on_scene_tree_node_added_removed.bind(is_added)): | ||||||
|  | 			get_tree().connect(signal_name, _on_scene_tree_node_added_removed.bind(is_added)) | ||||||
|  | 	connect_scene_tree_signal.call("node_added", true) | ||||||
|  | 	connect_scene_tree_signal.call("node_removed", false) | ||||||
|  | 
 | ||||||
|  | 	if not process_thread: | ||||||
|  | 		process_thread = ProcessThread.PHYSICS | ||||||
|  | 
 | ||||||
|  | 	if not actor: | ||||||
|  | 		if actor_node_path: | ||||||
|  | 			actor = get_node(actor_node_path) | ||||||
|  | 		else: | ||||||
|  | 			actor = get_parent() | ||||||
|  | 
 | ||||||
|  | 	if not blackboard: | ||||||
|  | 		# invoke setter to auto-initialise the blackboard. | ||||||
|  | 		self.blackboard = null | ||||||
|  | 
 | ||||||
|  | 	# Get the name of the parent node name for metric | ||||||
|  | 	_process_time_metric_name = ( | ||||||
|  | 		"beehave [microseconds]/process_time_%s-%s" % [actor.name, get_instance_id()] | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	set_physics_process(enabled and process_thread == ProcessThread.PHYSICS) | ||||||
|  | 	set_process(enabled and process_thread == ProcessThread.IDLE) | ||||||
|  | 
 | ||||||
|  | 	# Register custom metric to the engine | ||||||
|  | 	if custom_monitor and not Engine.is_editor_hint(): | ||||||
|  | 		Performance.add_custom_monitor(_process_time_metric_name, _get_process_time_metric_value) | ||||||
|  | 		_get_global_metrics().register_tree(self) | ||||||
|  | 
 | ||||||
|  | 	if Engine.is_editor_hint(): | ||||||
|  | 		update_configuration_warnings.call_deferred() | ||||||
|  | 	else: | ||||||
|  | 		_get_global_debugger().register_tree(self) | ||||||
|  | 		BeehaveDebuggerMessages.register_tree(_get_debugger_data(self)) | ||||||
|  | 
 | ||||||
|  | 	# Randomize at what frames tick() will happen to avoid stutters | ||||||
|  | 	last_tick = randi_range(0, tick_rate - 1) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_scene_tree_node_added_removed(node: Node, is_added: bool) -> void: | ||||||
|  | 	if Engine.is_editor_hint(): | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	if node is BeehaveNode and is_ancestor_of(node): | ||||||
|  | 		var sgnal := node.ready if is_added else node.tree_exited | ||||||
|  | 		if is_added: | ||||||
|  | 			sgnal.connect( | ||||||
|  | 				func() -> void: BeehaveDebuggerMessages.register_tree(_get_debugger_data(self)), | ||||||
|  | 				CONNECT_ONE_SHOT | ||||||
|  | 			) | ||||||
|  | 		else: | ||||||
|  | 			sgnal.connect( | ||||||
|  | 				func() -> void: | ||||||
|  | 					BeehaveDebuggerMessages.unregister_tree(get_instance_id()) | ||||||
|  | 					request_ready() | ||||||
|  | 			) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _physics_process(_delta: float) -> void: | ||||||
|  | 	_process_internally() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _process(_delta: float) -> void: | ||||||
|  | 	_process_internally() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _process_internally() -> void: | ||||||
|  | 	if Engine.is_editor_hint(): | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	if last_tick < tick_rate - 1: | ||||||
|  | 		last_tick += 1 | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	last_tick = 0 | ||||||
|  | 
 | ||||||
|  | 	# Start timing for metric | ||||||
|  | 	var start_time = Time.get_ticks_usec() | ||||||
|  | 
 | ||||||
|  | 	blackboard.set_value("can_send_message", _can_send_message) | ||||||
|  | 
 | ||||||
|  | 	if _can_send_message: | ||||||
|  | 		BeehaveDebuggerMessages.process_begin(get_instance_id()) | ||||||
|  | 
 | ||||||
|  | 	if self.get_child_count() == 1: | ||||||
|  | 		tick() | ||||||
|  | 
 | ||||||
|  | 	if _can_send_message: | ||||||
|  | 		BeehaveDebuggerMessages.process_end(get_instance_id()) | ||||||
|  | 
 | ||||||
|  | 	# Check the cost for this frame and save it for metric report | ||||||
|  | 	_process_time_metric_value = Time.get_ticks_usec() - start_time | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick() -> int: | ||||||
|  | 	if actor == null or get_child_count() == 0: | ||||||
|  | 		return FAILURE | ||||||
|  | 	var child := self.get_child(0) | ||||||
|  | 	if status != RUNNING: | ||||||
|  | 		child.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 	status = child.tick(actor, blackboard) | ||||||
|  | 	if _can_send_message: | ||||||
|  | 		BeehaveDebuggerMessages.process_tick(child.get_instance_id(), status) | ||||||
|  | 		BeehaveDebuggerMessages.process_tick(get_instance_id(), status) | ||||||
|  | 
 | ||||||
|  | 	# Clear running action if nothing is running | ||||||
|  | 	if status != RUNNING: | ||||||
|  | 		blackboard.set_value("running_action", null, str(actor.get_instance_id())) | ||||||
|  | 		child.after_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 	return status | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_configuration_warnings() -> PackedStringArray: | ||||||
|  | 	var warnings: PackedStringArray = [] | ||||||
|  | 
 | ||||||
|  | 	if actor == null: | ||||||
|  | 		warnings.append("Configure target node on tree") | ||||||
|  | 
 | ||||||
|  | 	if get_children().any(func(x): return not (x is BeehaveNode)): | ||||||
|  | 		warnings.append("All children of this node should inherit from BeehaveNode class.") | ||||||
|  | 
 | ||||||
|  | 	if get_child_count() != 1: | ||||||
|  | 		warnings.append("BeehaveTree should have exactly one child node.") | ||||||
|  | 
 | ||||||
|  | 	return warnings | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Returns the currently running action | ||||||
|  | func get_running_action() -> ActionLeaf: | ||||||
|  | 	return blackboard.get_value("running_action", null, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Returns the last condition that was executed | ||||||
|  | func get_last_condition() -> ConditionLeaf: | ||||||
|  | 	return blackboard.get_value("last_condition", null, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Returns the status of the last executed condition | ||||||
|  | func get_last_condition_status() -> String: | ||||||
|  | 	if blackboard.has_value("last_condition_status", str(actor.get_instance_id())): | ||||||
|  | 		var status = blackboard.get_value( | ||||||
|  | 			"last_condition_status", null, str(actor.get_instance_id()) | ||||||
|  | 		) | ||||||
|  | 		if status == SUCCESS: | ||||||
|  | 			return "SUCCESS" | ||||||
|  | 		elif status == FAILURE: | ||||||
|  | 			return "FAILURE" | ||||||
|  | 		else: | ||||||
|  | 			return "RUNNING" | ||||||
|  | 	return "" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## interrupts this tree if anything was running | ||||||
|  | func interrupt() -> void: | ||||||
|  | 	if self.get_child_count() != 0: | ||||||
|  | 		var first_child = self.get_child(0) | ||||||
|  | 		if "interrupt" in first_child: | ||||||
|  | 			first_child.interrupt(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Enables this tree. | ||||||
|  | func enable() -> void: | ||||||
|  | 	self.enabled = true | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Disables this tree. | ||||||
|  | func disable() -> void: | ||||||
|  | 	self.enabled = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _exit_tree() -> void: | ||||||
|  | 	if custom_monitor: | ||||||
|  | 		if _process_time_metric_name != "": | ||||||
|  | 			# Remove tree metric from the engine | ||||||
|  | 			Performance.remove_custom_monitor(_process_time_metric_name) | ||||||
|  | 			_get_global_metrics().unregister_tree(self) | ||||||
|  | 
 | ||||||
|  | 		BeehaveDebuggerMessages.unregister_tree(get_instance_id()) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Called by the engine to profile this tree | ||||||
|  | func _get_process_time_metric_value() -> int: | ||||||
|  | 	return int(_process_time_metric_value) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_debugger_data(node: Node) -> Dictionary: | ||||||
|  | 	if not (node is BeehaveTree or node is BeehaveNode): | ||||||
|  | 		return {} | ||||||
|  | 
 | ||||||
|  | 	var data := { | ||||||
|  | 		path = node.get_path(), | ||||||
|  | 		name = node.name, | ||||||
|  | 		type = node.get_class_name(), | ||||||
|  | 		id = str(node.get_instance_id()) | ||||||
|  | 	} | ||||||
|  | 	if node.get_child_count() > 0: | ||||||
|  | 		data.children = [] | ||||||
|  | 	for child in node.get_children(): | ||||||
|  | 		var child_data := _get_debugger_data(child) | ||||||
|  | 		if not child_data.is_empty(): | ||||||
|  | 			data.children.push_back(child_data) | ||||||
|  | 	return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	return [&"BeehaveTree"] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # required to avoid lifecycle issues on initial load | ||||||
|  | # due to loading order problems with autoloads | ||||||
|  | func _get_global_metrics() -> Node: | ||||||
|  | 	return get_tree().root.get_node("BeehaveGlobalMetrics") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # required to avoid lifecycle issues on initial load | ||||||
|  | # due to loading order problems with autoloads | ||||||
|  | func _get_global_debugger() -> Node: | ||||||
|  | 	return get_tree().root.get_node("BeehaveGlobalDebugger") | ||||||
							
								
								
									
										34
									
								
								addons/beehave/nodes/composites/composite.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								addons/beehave/nodes/composites/composite.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/category_composite.svg") | ||||||
|  | class_name Composite extends BeehaveNode | ||||||
|  | 
 | ||||||
|  | ## A Composite node controls the flow of execution of its children in a specific manner. | ||||||
|  | 
 | ||||||
|  | var running_child: BeehaveNode = null | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_configuration_warnings() -> PackedStringArray: | ||||||
|  | 	var warnings: PackedStringArray = super._get_configuration_warnings() | ||||||
|  | 
 | ||||||
|  | 	if get_children().filter(func(x): return x is BeehaveNode).size() < 2: | ||||||
|  | 		warnings.append( | ||||||
|  | 			"Any composite node should have at least two children. Otherwise it is not useful." | ||||||
|  | 		) | ||||||
|  | 
 | ||||||
|  | 	return warnings | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func interrupt(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	if running_child != null: | ||||||
|  | 		running_child.interrupt(actor, blackboard) | ||||||
|  | 		running_child = null | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func after_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	running_child = null | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"Composite") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										176
									
								
								addons/beehave/nodes/composites/randomized_composite.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								addons/beehave/nodes/composites/randomized_composite.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | |||||||
|  | @tool | ||||||
|  | class_name RandomizedComposite extends Composite | ||||||
|  | 
 | ||||||
|  | const WEIGHTS_PREFIX = "Weights/" | ||||||
|  | 
 | ||||||
|  | ## Sets a predicable seed | ||||||
|  | @export var random_seed: int = 0: | ||||||
|  | 	set(rs): | ||||||
|  | 		random_seed = rs | ||||||
|  | 		if random_seed != 0: | ||||||
|  | 			seed(random_seed) | ||||||
|  | 		else: | ||||||
|  | 			randomize() | ||||||
|  | 
 | ||||||
|  | ## Wether to use weights for every child or not. | ||||||
|  | @export var use_weights: bool: | ||||||
|  | 	set(value): | ||||||
|  | 		use_weights = value | ||||||
|  | 		if use_weights: | ||||||
|  | 			_update_weights(get_children()) | ||||||
|  | 			_connect_children_changing_signals() | ||||||
|  | 		notify_property_list_changed() | ||||||
|  | 
 | ||||||
|  | var _weights: Dictionary | ||||||
|  | var _exiting_tree: bool | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _ready(): | ||||||
|  | 	_connect_children_changing_signals() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _connect_children_changing_signals(): | ||||||
|  | 	if not child_entered_tree.is_connected(_on_child_entered_tree): | ||||||
|  | 		child_entered_tree.connect(_on_child_entered_tree) | ||||||
|  | 
 | ||||||
|  | 	if not child_exiting_tree.is_connected(_on_child_exiting_tree): | ||||||
|  | 		child_exiting_tree.connect(_on_child_exiting_tree) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_shuffled_children() -> Array[Node]: | ||||||
|  | 	var children_bag: Array[Node] = get_children().duplicate() | ||||||
|  | 	if use_weights: | ||||||
|  | 		var weights: Array[int] | ||||||
|  | 		weights.assign(children_bag.map(func(child): return _weights[child.name])) | ||||||
|  | 		children_bag.assign(_weighted_shuffle(children_bag, weights)) | ||||||
|  | 	else: | ||||||
|  | 		children_bag.shuffle() | ||||||
|  | 	return children_bag | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Returns a shuffled version of a given array using the supplied array of weights. | ||||||
|  | ## Think of weights as the chance of a given item being the first in the array. | ||||||
|  | func _weighted_shuffle(items: Array, weights: Array[int]) -> Array: | ||||||
|  | 	if len(items) != len(weights): | ||||||
|  | 		push_error( | ||||||
|  | 			( | ||||||
|  | 				"items and weights size mismatch: expected %d weights, got %d instead." | ||||||
|  | 				% [len(items), len(weights)] | ||||||
|  | 			) | ||||||
|  | 		) | ||||||
|  | 		return items | ||||||
|  | 
 | ||||||
|  | 	# This method is based on the weighted random sampling algorithm | ||||||
|  | 	# by Efraimidis, Spirakis; 2005. This runs in O(n log(n)). | ||||||
|  | 
 | ||||||
|  | 	# For each index, it will calculate random_value^(1/weight). | ||||||
|  | 	var chance_calc = func(i): return [i, randf() ** (1.0 / weights[i])] | ||||||
|  | 	var random_distribuition = range(len(items)).map(chance_calc) | ||||||
|  | 
 | ||||||
|  | 	# Now we just have to order by the calculated value, descending. | ||||||
|  | 	random_distribuition.sort_custom(func(a, b): return a[1] > b[1]) | ||||||
|  | 
 | ||||||
|  | 	return random_distribuition.map(func(dist): return items[dist[0]]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_property_list(): | ||||||
|  | 	var properties = [] | ||||||
|  | 
 | ||||||
|  | 	if use_weights: | ||||||
|  | 		for key in _weights.keys(): | ||||||
|  | 			properties.append( | ||||||
|  | 				{ | ||||||
|  | 					"name": WEIGHTS_PREFIX + key, | ||||||
|  | 					"type": TYPE_INT, | ||||||
|  | 					"usage": PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR, | ||||||
|  | 					"hint": PROPERTY_HINT_RANGE, | ||||||
|  | 					"hint_string": "1,100" | ||||||
|  | 				} | ||||||
|  | 			) | ||||||
|  | 
 | ||||||
|  | 	return properties | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _set(property: StringName, value: Variant) -> bool: | ||||||
|  | 	if property.begins_with(WEIGHTS_PREFIX): | ||||||
|  | 		var weight_name = property.trim_prefix(WEIGHTS_PREFIX) | ||||||
|  | 		_weights[weight_name] = value | ||||||
|  | 		return true | ||||||
|  | 
 | ||||||
|  | 	return false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get(property: StringName): | ||||||
|  | 	if property.begins_with(WEIGHTS_PREFIX): | ||||||
|  | 		var weight_name = property.trim_prefix(WEIGHTS_PREFIX) | ||||||
|  | 		return _weights[weight_name] | ||||||
|  | 
 | ||||||
|  | 	return null | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _update_weights(children: Array[Node]) -> void: | ||||||
|  | 	var new_weights = {} | ||||||
|  | 	for c in children: | ||||||
|  | 		if _weights.has(c.name): | ||||||
|  | 			new_weights[c.name] = _weights[c.name] | ||||||
|  | 		else: | ||||||
|  | 			new_weights[c.name] = 1 | ||||||
|  | 	_weights = new_weights | ||||||
|  | 	notify_property_list_changed() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _exit_tree() -> void: | ||||||
|  | 	_exiting_tree = true | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _enter_tree() -> void: | ||||||
|  | 	_exiting_tree = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_child_entered_tree(node: Node): | ||||||
|  | 	_update_weights(get_children()) | ||||||
|  | 
 | ||||||
|  | 	var renamed_callable = _on_child_renamed.bind(node.name, node) | ||||||
|  | 	if not node.renamed.is_connected(renamed_callable): | ||||||
|  | 		node.renamed.connect(renamed_callable) | ||||||
|  | 
 | ||||||
|  | 	if not node.tree_exited.is_connected(_on_child_tree_exited): | ||||||
|  | 		node.tree_exited.connect(_on_child_tree_exited.bind(node)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_child_exiting_tree(node: Node): | ||||||
|  | 	var renamed_callable = _on_child_renamed.bind(node.name, node) | ||||||
|  | 	if node.renamed.is_connected(renamed_callable): | ||||||
|  | 		node.renamed.disconnect(renamed_callable) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_child_tree_exited(node: Node) -> void: | ||||||
|  | 	# don't erase the individual child if the whole tree is exiting together | ||||||
|  | 	if not _exiting_tree: | ||||||
|  | 		var children = get_children() | ||||||
|  | 		children.erase(node) | ||||||
|  | 		_update_weights(children) | ||||||
|  | 
 | ||||||
|  | 	if node.tree_exited.is_connected(_on_child_tree_exited): | ||||||
|  | 		node.tree_exited.disconnect(_on_child_tree_exited) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_child_renamed(old_name: String, renamed_child: Node): | ||||||
|  | 	if old_name == renamed_child.name: | ||||||
|  | 		return  # No need to update the weights. | ||||||
|  | 
 | ||||||
|  | 	# Disconnect signal with old name... | ||||||
|  | 	renamed_child.renamed.disconnect(_on_child_renamed.bind(old_name, renamed_child)) | ||||||
|  | 	# ...and connect with the new name. | ||||||
|  | 	renamed_child.renamed.connect(_on_child_renamed.bind(renamed_child.name, renamed_child)) | ||||||
|  | 
 | ||||||
|  | 	var original_weight = _weights[old_name] | ||||||
|  | 	_weights.erase(old_name) | ||||||
|  | 	_weights[renamed_child.name] = original_weight | ||||||
|  | 	notify_property_list_changed() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"RandomizedComposite") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										69
									
								
								addons/beehave/nodes/composites/selector.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								addons/beehave/nodes/composites/selector.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/selector.svg") | ||||||
|  | class_name SelectorComposite extends Composite | ||||||
|  | 
 | ||||||
|  | ## Selector nodes will attempt to execute each of its children until one of | ||||||
|  | ## them return `SUCCESS`. If all children return `FAILURE`, this node will also | ||||||
|  | ## return `FAILURE`. | ||||||
|  | ## If a child returns `RUNNING` it will tick again. | ||||||
|  | 
 | ||||||
|  | var last_execution_index: int = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	for c in get_children(): | ||||||
|  | 		if c.get_index() < last_execution_index: | ||||||
|  | 			continue | ||||||
|  | 
 | ||||||
|  | 		if c != running_child: | ||||||
|  | 			c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		var response = c.tick(actor, blackboard) | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if c is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		match response: | ||||||
|  | 			SUCCESS: | ||||||
|  | 				_cleanup_running_task(c, actor, blackboard) | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 				return SUCCESS | ||||||
|  | 			FAILURE: | ||||||
|  | 				_cleanup_running_task(c, actor, blackboard) | ||||||
|  | 				last_execution_index += 1 | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 			RUNNING: | ||||||
|  | 				running_child = c | ||||||
|  | 				if c is ActionLeaf: | ||||||
|  | 					blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 				return RUNNING | ||||||
|  | 
 | ||||||
|  | 	return FAILURE | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func after_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	last_execution_index = 0 | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func interrupt(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	last_execution_index = 0 | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Changes `running_action` and `running_child` after the node finishes executing. | ||||||
|  | func _cleanup_running_task(finished_action: Node, actor: Node, blackboard: Blackboard): | ||||||
|  | 	var blackboard_name = str(actor.get_instance_id()) | ||||||
|  | 	if finished_action == running_child: | ||||||
|  | 		running_child = null | ||||||
|  | 		if finished_action == blackboard.get_value("running_action", null, blackboard_name): | ||||||
|  | 			blackboard.set_value("running_action", null, blackboard_name) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"SelectorComposite") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										82
									
								
								addons/beehave/nodes/composites/selector_random.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								addons/beehave/nodes/composites/selector_random.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/selector_random.svg") | ||||||
|  | class_name SelectorRandomComposite extends RandomizedComposite | ||||||
|  | 
 | ||||||
|  | ## 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. | ||||||
|  | 
 | ||||||
|  | ## A shuffled list of the children that will be executed in reverse order. | ||||||
|  | var _children_bag: Array[Node] = [] | ||||||
|  | var c: Node | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _ready() -> void: | ||||||
|  | 	super() | ||||||
|  | 	if random_seed == 0: | ||||||
|  | 		randomize() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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] | ||||||
|  | 
 | ||||||
|  | 		if c != running_child: | ||||||
|  | 			c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		var response = c.tick(actor, blackboard) | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if c is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		match response: | ||||||
|  | 			SUCCESS: | ||||||
|  | 				_children_bag.erase(c) | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 				return SUCCESS | ||||||
|  | 			FAILURE: | ||||||
|  | 				_children_bag.erase(c) | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 			RUNNING: | ||||||
|  | 				running_child = c | ||||||
|  | 				if c is ActionLeaf: | ||||||
|  | 					blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 				return RUNNING | ||||||
|  | 
 | ||||||
|  | 	return FAILURE | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func after_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	_reset() | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func interrupt(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	_reset() | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_reversed_indexes() -> Array[int]: | ||||||
|  | 	var reversed: Array[int] | ||||||
|  | 	reversed.assign(range(_children_bag.size())) | ||||||
|  | 	reversed.reverse() | ||||||
|  | 	return reversed | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _reset() -> void: | ||||||
|  | 	var new_order = get_shuffled_children() | ||||||
|  | 	_children_bag = new_order.duplicate() | ||||||
|  | 	_children_bag.reverse()  # It needs to run the children in reverse order. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"SelectorRandomComposite") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										47
									
								
								addons/beehave/nodes/composites/selector_reactive.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								addons/beehave/nodes/composites/selector_reactive.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/selector_reactive.svg") | ||||||
|  | class_name SelectorReactiveComposite extends Composite | ||||||
|  | 
 | ||||||
|  | ## Selector Reactive nodes will attempt to execute each of its children until one of | ||||||
|  | ## them return `SUCCESS`. If all children return `FAILURE`, this node will also | ||||||
|  | ## return `FAILURE`. | ||||||
|  | ## If a child returns `RUNNING` it will restart. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	for c in get_children(): | ||||||
|  | 		if c != running_child: | ||||||
|  | 			c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		var response = c.tick(actor, blackboard) | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if c is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		match response: | ||||||
|  | 			SUCCESS: | ||||||
|  | 				# Interrupt any child that was RUNNING before. | ||||||
|  | 				if c != running_child: | ||||||
|  | 					interrupt(actor, blackboard) | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 				return SUCCESS | ||||||
|  | 			FAILURE: | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 			RUNNING: | ||||||
|  | 				if c != running_child: | ||||||
|  | 					interrupt(actor, blackboard) | ||||||
|  | 					running_child = c | ||||||
|  | 				if c is ActionLeaf: | ||||||
|  | 					blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 				return RUNNING | ||||||
|  | 
 | ||||||
|  | 	return FAILURE | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"SelectorReactiveComposite") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										75
									
								
								addons/beehave/nodes/composites/sequence.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								addons/beehave/nodes/composites/sequence.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/sequence.svg") | ||||||
|  | class_name SequenceComposite extends Composite | ||||||
|  | 
 | ||||||
|  | ## Sequence nodes will attempt to execute all of its children and report | ||||||
|  | ## `SUCCESS` in case all of the children report a `SUCCESS` status code. | ||||||
|  | ## If at least one child reports a `FAILURE` status code, this node will also | ||||||
|  | ## return `FAILURE` and restart. | ||||||
|  | ## In case a child returns `RUNNING` this node will tick again. | ||||||
|  | 
 | ||||||
|  | var successful_index: int = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	for c in get_children(): | ||||||
|  | 		if c.get_index() < successful_index: | ||||||
|  | 			continue | ||||||
|  | 
 | ||||||
|  | 		if c != running_child: | ||||||
|  | 			c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		var response = c.tick(actor, blackboard) | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if c is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		match response: | ||||||
|  | 			SUCCESS: | ||||||
|  | 				_cleanup_running_task(c, actor, blackboard) | ||||||
|  | 				successful_index += 1 | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 			FAILURE: | ||||||
|  | 				_cleanup_running_task(c, actor, blackboard) | ||||||
|  | 				# Interrupt any child that was RUNNING before. | ||||||
|  | 				interrupt(actor, blackboard) | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 				return FAILURE | ||||||
|  | 			RUNNING: | ||||||
|  | 				if c != running_child: | ||||||
|  | 					if running_child != null: | ||||||
|  | 						running_child.interrupt(actor, blackboard) | ||||||
|  | 					running_child = c | ||||||
|  | 				if c is ActionLeaf: | ||||||
|  | 					blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 				return RUNNING | ||||||
|  | 
 | ||||||
|  | 	_reset() | ||||||
|  | 	return SUCCESS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func interrupt(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	_reset() | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _reset() -> void: | ||||||
|  | 	successful_index = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Changes `running_action` and `running_child` after the node finishes executing. | ||||||
|  | func _cleanup_running_task(finished_action: Node, actor: Node, blackboard: Blackboard): | ||||||
|  | 	var blackboard_name = str(actor.get_instance_id()) | ||||||
|  | 	if finished_action == running_child: | ||||||
|  | 		running_child = null | ||||||
|  | 		if finished_action == blackboard.get_value("running_action", null, blackboard_name): | ||||||
|  | 			blackboard.set_value("running_action", null, blackboard_name) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"SequenceComposite") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										96
									
								
								addons/beehave/nodes/composites/sequence_random.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								addons/beehave/nodes/composites/sequence_random.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,96 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/sequence_random.svg") | ||||||
|  | class_name SequenceRandomComposite extends RandomizedComposite | ||||||
|  | 
 | ||||||
|  | ## 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. | ||||||
|  | 
 | ||||||
|  | # Emitted whenever the children are shuffled. | ||||||
|  | signal reset(new_order: Array[Node]) | ||||||
|  | 
 | ||||||
|  | ## 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 _ready() -> void: | ||||||
|  | 	super() | ||||||
|  | 	if random_seed == 0: | ||||||
|  | 		randomize() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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] | ||||||
|  | 
 | ||||||
|  | 		if c != running_child: | ||||||
|  | 			c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		var response = c.tick(actor, blackboard) | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if c is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		match response: | ||||||
|  | 			SUCCESS: | ||||||
|  | 				_children_bag.erase(c) | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 			FAILURE: | ||||||
|  | 				_children_bag.erase(c) | ||||||
|  | 				# Interrupt any child that was RUNNING before | ||||||
|  | 				# but do not reset! | ||||||
|  | 				super.interrupt(actor, blackboard) | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 				return FAILURE | ||||||
|  | 			RUNNING: | ||||||
|  | 				running_child = c | ||||||
|  | 				if c is ActionLeaf: | ||||||
|  | 					blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 				return RUNNING | ||||||
|  | 
 | ||||||
|  | 	return SUCCESS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func after_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	if not resume_on_failure: | ||||||
|  | 		_reset() | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func interrupt(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	if not resume_on_interrupt: | ||||||
|  | 		_reset() | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_reversed_indexes() -> Array[int]: | ||||||
|  | 	var reversed: Array[int] | ||||||
|  | 	reversed.assign(range(_children_bag.size())) | ||||||
|  | 	reversed.reverse() | ||||||
|  | 	return reversed | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _reset() -> void: | ||||||
|  | 	var new_order = get_shuffled_children() | ||||||
|  | 	_children_bag = new_order.duplicate() | ||||||
|  | 	_children_bag.reverse()  # It needs to run the children in reverse order. | ||||||
|  | 	reset.emit(new_order) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"SequenceRandomComposite") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										63
									
								
								addons/beehave/nodes/composites/sequence_reactive.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								addons/beehave/nodes/composites/sequence_reactive.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/sequence_reactive.svg") | ||||||
|  | class_name SequenceReactiveComposite extends Composite | ||||||
|  | 
 | ||||||
|  | ## Reactive Sequence nodes will attempt to execute all of its children and report | ||||||
|  | ## `SUCCESS` in case all of the children report a `SUCCESS` status code. | ||||||
|  | ## If at least one child reports a `FAILURE` status code, this node will also | ||||||
|  | ## return `FAILURE` and restart. | ||||||
|  | ## In case a child returns `RUNNING` this node will restart. | ||||||
|  | 
 | ||||||
|  | var successful_index: int = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	for c in get_children(): | ||||||
|  | 		if c.get_index() < successful_index: | ||||||
|  | 			continue | ||||||
|  | 
 | ||||||
|  | 		if c != running_child: | ||||||
|  | 			c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		var response = c.tick(actor, blackboard) | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if c is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		match response: | ||||||
|  | 			SUCCESS: | ||||||
|  | 				successful_index += 1 | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 			FAILURE: | ||||||
|  | 				# Interrupt any child that was RUNNING before. | ||||||
|  | 				interrupt(actor, blackboard) | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 				return FAILURE | ||||||
|  | 			RUNNING: | ||||||
|  | 				_reset() | ||||||
|  | 				if running_child != c: | ||||||
|  | 					interrupt(actor, blackboard) | ||||||
|  | 					running_child = c | ||||||
|  | 				if c is ActionLeaf: | ||||||
|  | 					blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 				return RUNNING | ||||||
|  | 	_reset() | ||||||
|  | 	return SUCCESS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func interrupt(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	_reset() | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _reset() -> void: | ||||||
|  | 	successful_index = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"SequenceReactiveComposite") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										61
									
								
								addons/beehave/nodes/composites/sequence_star.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								addons/beehave/nodes/composites/sequence_star.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/sequence_reactive.svg") | ||||||
|  | class_name SequenceStarComposite extends Composite | ||||||
|  | 
 | ||||||
|  | ## Sequence Star nodes will attempt to execute all of its children and report | ||||||
|  | ## `SUCCESS` in case all of the children report a `SUCCESS` status code. | ||||||
|  | ## If at least one child reports a `FAILURE` status code, this node will also | ||||||
|  | ## return `FAILURE` and tick again. | ||||||
|  | ## In case a child returns `RUNNING` this node will tick again. | ||||||
|  | 
 | ||||||
|  | var successful_index: int = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	for c in get_children(): | ||||||
|  | 		if c.get_index() < successful_index: | ||||||
|  | 			continue | ||||||
|  | 
 | ||||||
|  | 		if c != running_child: | ||||||
|  | 			c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		var response = c.tick(actor, blackboard) | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if c is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		match response: | ||||||
|  | 			SUCCESS: | ||||||
|  | 				successful_index += 1 | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 			FAILURE: | ||||||
|  | 				# Interrupt any child that was RUNNING before | ||||||
|  | 				# but do not reset! | ||||||
|  | 				super.interrupt(actor, blackboard) | ||||||
|  | 				c.after_run(actor, blackboard) | ||||||
|  | 				return FAILURE | ||||||
|  | 			RUNNING: | ||||||
|  | 				running_child = c | ||||||
|  | 				if c is ActionLeaf: | ||||||
|  | 					blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 				return RUNNING | ||||||
|  | 	_reset() | ||||||
|  | 	return SUCCESS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func interrupt(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	_reset() | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _reset() -> void: | ||||||
|  | 	successful_index = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"SequenceStarComposite") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										122
									
								
								addons/beehave/nodes/composites/simple_parallel.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								addons/beehave/nodes/composites/simple_parallel.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/simple_parallel.svg") | ||||||
|  | class_name SimpleParallelComposite extends Composite | ||||||
|  | 
 | ||||||
|  | ## Simple Parallel nodes will attampt to execute all chidren at same time and | ||||||
|  | ## can only have exactly two children. First child as primary node, second | ||||||
|  | ## child as secondary node. | ||||||
|  | ## This node will always report primary node's state, and continue tick while | ||||||
|  | ## primary node return 'RUNNING'. The state of secondary node will be ignored | ||||||
|  | ## and executed like a subtree. | ||||||
|  | ## If primary node return 'SUCCESS' or 'FAILURE', this node will interrupt | ||||||
|  | ## secondary node and return primary node's result. | ||||||
|  | ## If this node is running under delay mode, it will wait seconday node | ||||||
|  | ## finish its action after primary node terminates. | ||||||
|  | 
 | ||||||
|  | #how many times should secondary node repeat, zero means loop forever | ||||||
|  | @export var secondary_node_repeat_count: int = 0 | ||||||
|  | 
 | ||||||
|  | #wether to wait secondary node finish its current action after primary node finished | ||||||
|  | @export var delay_mode: bool = false | ||||||
|  | 
 | ||||||
|  | var delayed_result := SUCCESS | ||||||
|  | var main_task_finished: bool = false | ||||||
|  | var secondary_node_running: bool = false | ||||||
|  | var secondary_node_repeat_left: int = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_configuration_warnings() -> PackedStringArray: | ||||||
|  | 	var warnings: PackedStringArray = super._get_configuration_warnings() | ||||||
|  | 
 | ||||||
|  | 	if get_child_count() != 2: | ||||||
|  | 		warnings.append("SimpleParallel should have exactly two child nodes.") | ||||||
|  | 
 | ||||||
|  | 	if not get_child(0) is ActionLeaf: | ||||||
|  | 		warnings.append("SimpleParallel should have an action leaf node as first child node.") | ||||||
|  | 
 | ||||||
|  | 	return warnings | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor, blackboard: Blackboard): | ||||||
|  | 	for c in get_children(): | ||||||
|  | 		var node_index = c.get_index() | ||||||
|  | 		if node_index == 0 and not main_task_finished: | ||||||
|  | 			if c != running_child: | ||||||
|  | 				c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 			var response = c.tick(actor, blackboard) | ||||||
|  | 			if can_send_message(blackboard): | ||||||
|  | 				BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 			delayed_result = response | ||||||
|  | 			match response: | ||||||
|  | 				SUCCESS, FAILURE: | ||||||
|  | 					_cleanup_running_task(c, actor, blackboard) | ||||||
|  | 					c.after_run(actor, blackboard) | ||||||
|  | 					main_task_finished = true | ||||||
|  | 					if not delay_mode: | ||||||
|  | 						if secondary_node_running: | ||||||
|  | 							get_child(1).interrupt(actor, blackboard) | ||||||
|  | 						_reset() | ||||||
|  | 						return delayed_result | ||||||
|  | 				RUNNING: | ||||||
|  | 					running_child = c | ||||||
|  | 					if c is ActionLeaf: | ||||||
|  | 						blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		elif node_index == 1: | ||||||
|  | 			if secondary_node_repeat_count == 0 or secondary_node_repeat_left > 0: | ||||||
|  | 				if not secondary_node_running: | ||||||
|  | 					c.before_run(actor, blackboard) | ||||||
|  | 				var subtree_response = c.tick(actor, blackboard) | ||||||
|  | 				if subtree_response != RUNNING: | ||||||
|  | 					secondary_node_running = false | ||||||
|  | 					c.after_run(actor, blackboard) | ||||||
|  | 					if delay_mode and main_task_finished: | ||||||
|  | 						_reset() | ||||||
|  | 						return delayed_result | ||||||
|  | 					elif secondary_node_repeat_left > 0: | ||||||
|  | 						secondary_node_repeat_left -= 1 | ||||||
|  | 				else: | ||||||
|  | 					secondary_node_running = true | ||||||
|  | 
 | ||||||
|  | 	return RUNNING | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func before_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	secondary_node_repeat_left = secondary_node_repeat_count | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func interrupt(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	if not main_task_finished: | ||||||
|  | 		get_child(0).interrupt(actor, blackboard) | ||||||
|  | 	if secondary_node_running: | ||||||
|  | 		get_child(1).interrupt(actor, blackboard) | ||||||
|  | 	_reset() | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func after_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	_reset() | ||||||
|  | 	super(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _reset() -> void: | ||||||
|  | 	main_task_finished = false | ||||||
|  | 	secondary_node_running = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Changes `running_action` and `running_child` after the node finishes executing. | ||||||
|  | func _cleanup_running_task(finished_action: Node, actor: Node, blackboard: Blackboard): | ||||||
|  | 	var blackboard_name = str(actor.get_instance_id()) | ||||||
|  | 	if finished_action == running_child: | ||||||
|  | 		running_child = null | ||||||
|  | 		if finished_action == blackboard.get_value("running_action", null, blackboard_name): | ||||||
|  | 			blackboard.set_value("running_action", null, blackboard_name) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"SimpleParallelComposite") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										49
									
								
								addons/beehave/nodes/decorators/cooldown.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								addons/beehave/nodes/decorators/cooldown.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/cooldown.svg") | ||||||
|  | extends Decorator | ||||||
|  | class_name CooldownDecorator | ||||||
|  | 
 | ||||||
|  | ## The Cooldown Decorator will return 'FAILURE' for a set amount of time | ||||||
|  | ## after executing its child. | ||||||
|  | ## The timer resets the next time its child is executed and it is not `RUNNING` | ||||||
|  | 
 | ||||||
|  | ## The wait time in seconds | ||||||
|  | @export var wait_time := 0.0 | ||||||
|  | 
 | ||||||
|  | @onready var cache_key = "cooldown_%s" % self.get_instance_id() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var c = get_child(0) | ||||||
|  | 	var remaining_time = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id())) | ||||||
|  | 	var response | ||||||
|  | 
 | ||||||
|  | 	if c != running_child: | ||||||
|  | 		c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 	if remaining_time > 0: | ||||||
|  | 		response = FAILURE | ||||||
|  | 
 | ||||||
|  | 		remaining_time -= get_physics_process_delta_time() | ||||||
|  | 		blackboard.set_value(cache_key, remaining_time, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(self.get_instance_id(), response) | ||||||
|  | 	else: | ||||||
|  | 		response = c.tick(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if c is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		if response == RUNNING and c is ActionLeaf: | ||||||
|  | 			running_child = c | ||||||
|  | 			blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		if response != RUNNING: | ||||||
|  | 			blackboard.set_value(cache_key, wait_time, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 	return response | ||||||
							
								
								
									
										33
									
								
								addons/beehave/nodes/decorators/decorator.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								addons/beehave/nodes/decorators/decorator.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/category_decorator.svg") | ||||||
|  | class_name Decorator extends BeehaveNode | ||||||
|  | 
 | ||||||
|  | ## Decorator nodes are used to transform the result received by its child. | ||||||
|  | ## Must only have one child. | ||||||
|  | 
 | ||||||
|  | var running_child: BeehaveNode = null | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_configuration_warnings() -> PackedStringArray: | ||||||
|  | 	var warnings: PackedStringArray = super._get_configuration_warnings() | ||||||
|  | 
 | ||||||
|  | 	if get_child_count() != 1: | ||||||
|  | 		warnings.append("Decorator should have exactly one child node.") | ||||||
|  | 
 | ||||||
|  | 	return warnings | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func interrupt(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	if running_child != null: | ||||||
|  | 		running_child.interrupt(actor, blackboard) | ||||||
|  | 		running_child = null | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func after_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	running_child = null | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"Decorator") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										49
									
								
								addons/beehave/nodes/decorators/delayer.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								addons/beehave/nodes/decorators/delayer.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/delayer.svg") | ||||||
|  | extends Decorator | ||||||
|  | class_name DelayDecorator | ||||||
|  | 
 | ||||||
|  | ## The Delay Decorator will return 'RUNNING' for a set amount of time | ||||||
|  | ## before executing its child. | ||||||
|  | ## The timer resets when both it and its child are not `RUNNING` | ||||||
|  | 
 | ||||||
|  | ## The wait time in seconds | ||||||
|  | @export var wait_time := 0.0 | ||||||
|  | 
 | ||||||
|  | @onready var cache_key = "time_limiter_%s" % self.get_instance_id() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var c = get_child(0) | ||||||
|  | 	var total_time = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id())) | ||||||
|  | 	var response | ||||||
|  | 
 | ||||||
|  | 	if c != running_child: | ||||||
|  | 		c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 	if total_time < wait_time: | ||||||
|  | 		response = RUNNING | ||||||
|  | 
 | ||||||
|  | 		total_time += get_physics_process_delta_time() | ||||||
|  | 		blackboard.set_value(cache_key, total_time, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(self.get_instance_id(), response) | ||||||
|  | 	else: | ||||||
|  | 		response = c.tick(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if c is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		if response == RUNNING and c is ActionLeaf: | ||||||
|  | 			running_child = c | ||||||
|  | 			blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		if response != RUNNING: | ||||||
|  | 			blackboard.set_value(cache_key, 0.0, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 	return response | ||||||
							
								
								
									
										35
									
								
								addons/beehave/nodes/decorators/failer.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								addons/beehave/nodes/decorators/failer.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/failer.svg") | ||||||
|  | class_name AlwaysFailDecorator extends Decorator | ||||||
|  | 
 | ||||||
|  | ## A Failer node will always return a `FAILURE` status code. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var c = get_child(0) | ||||||
|  | 
 | ||||||
|  | 	if c != running_child: | ||||||
|  | 		c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 	var response = c.tick(actor, blackboard) | ||||||
|  | 	if can_send_message(blackboard): | ||||||
|  | 		BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 	if c is ConditionLeaf: | ||||||
|  | 		blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 		blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 	if response == RUNNING: | ||||||
|  | 		running_child = c | ||||||
|  | 		if c is ActionLeaf: | ||||||
|  | 			blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 		return RUNNING | ||||||
|  | 	else: | ||||||
|  | 		c.after_run(actor, blackboard) | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"AlwaysFailDecorator") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										43
									
								
								addons/beehave/nodes/decorators/inverter.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								addons/beehave/nodes/decorators/inverter.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/inverter.svg") | ||||||
|  | class_name InverterDecorator extends Decorator | ||||||
|  | 
 | ||||||
|  | ## An inverter will return `FAILURE` in case it's child returns a `SUCCESS` status | ||||||
|  | ## code or `SUCCESS` in case its child returns a `FAILURE` status code. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var c = get_child(0) | ||||||
|  | 
 | ||||||
|  | 	if c != running_child: | ||||||
|  | 		c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 	var response = c.tick(actor, blackboard) | ||||||
|  | 	if can_send_message(blackboard): | ||||||
|  | 		BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 	if c is ConditionLeaf: | ||||||
|  | 		blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 		blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 	match response: | ||||||
|  | 		SUCCESS: | ||||||
|  | 			c.after_run(actor, blackboard) | ||||||
|  | 			return FAILURE | ||||||
|  | 		FAILURE: | ||||||
|  | 			c.after_run(actor, blackboard) | ||||||
|  | 			return SUCCESS | ||||||
|  | 		RUNNING: | ||||||
|  | 			running_child = c | ||||||
|  | 			if c is ActionLeaf: | ||||||
|  | 				blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 			return RUNNING | ||||||
|  | 		_: | ||||||
|  | 			push_error("This should be unreachable") | ||||||
|  | 			return -1 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"InverterDecorator") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										60
									
								
								addons/beehave/nodes/decorators/limiter.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								addons/beehave/nodes/decorators/limiter.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/limiter.svg") | ||||||
|  | class_name LimiterDecorator extends Decorator | ||||||
|  | 
 | ||||||
|  | ## The limiter will execute its `RUNNING` child `x` amount of times. When the number of | ||||||
|  | ## maximum ticks is reached, it will return a `FAILURE` status code. | ||||||
|  | ## The count resets the next time that a child is not `RUNNING` | ||||||
|  | 
 | ||||||
|  | @onready var cache_key = "limiter_%s" % self.get_instance_id() | ||||||
|  | 
 | ||||||
|  | @export var max_count: float = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	if not get_child_count() == 1: | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 	var child = get_child(0) | ||||||
|  | 	var current_count = blackboard.get_value(cache_key, 0, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 	if current_count < max_count: | ||||||
|  | 		blackboard.set_value(cache_key, current_count + 1, str(actor.get_instance_id())) | ||||||
|  | 		var response = child.tick(actor, blackboard) | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if child is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", child, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		if child is ActionLeaf and response == RUNNING: | ||||||
|  | 			running_child = child | ||||||
|  | 			blackboard.set_value("running_action", child, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		if response != RUNNING: | ||||||
|  | 			child.after_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		return response | ||||||
|  | 	else: | ||||||
|  | 		interrupt(actor, blackboard) | ||||||
|  | 		child.after_run(actor, blackboard) | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func before_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	blackboard.set_value(cache_key, 0, str(actor.get_instance_id())) | ||||||
|  | 	if get_child_count() > 0: | ||||||
|  | 		get_child(0).before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"LimiterDecorator") | ||||||
|  | 	return classes | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_configuration_warnings() -> PackedStringArray: | ||||||
|  | 	if not get_child_count() == 1: | ||||||
|  | 		return ["Requires exactly one child node"] | ||||||
|  | 	return [] | ||||||
							
								
								
									
										58
									
								
								addons/beehave/nodes/decorators/repeater.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								addons/beehave/nodes/decorators/repeater.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | |||||||
|  | ## The repeater will execute its child until it returns `SUCCESS` a certain amount of times. | ||||||
|  | ## When the number of maximum ticks is reached, it will return a `SUCCESS` status code. | ||||||
|  | ## If the child returns `FAILURE`, the repeater will return `FAILURE` immediately. | ||||||
|  | @tool | ||||||
|  | @icon("../../icons/repeater.svg") | ||||||
|  | class_name RepeaterDecorator extends Decorator | ||||||
|  | 
 | ||||||
|  | @export var repetitions: int = 1 | ||||||
|  | var current_count: int = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func before_run(actor: Node, blackboard: Blackboard): | ||||||
|  | 	current_count = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var child = get_child(0) | ||||||
|  | 
 | ||||||
|  | 	if current_count < repetitions: | ||||||
|  | 		if running_child == null: | ||||||
|  | 			child.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		var response = child.tick(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if child is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", child, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		if response == RUNNING: | ||||||
|  | 			running_child = child | ||||||
|  | 			if child is ActionLeaf: | ||||||
|  | 				blackboard.set_value("running_action", child, str(actor.get_instance_id())) | ||||||
|  | 			return RUNNING | ||||||
|  | 
 | ||||||
|  | 		current_count += 1 | ||||||
|  | 		child.after_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 		if running_child != null: | ||||||
|  | 			running_child = null | ||||||
|  | 
 | ||||||
|  | 		if response == FAILURE: | ||||||
|  | 			return FAILURE | ||||||
|  | 
 | ||||||
|  | 		if current_count >= repetitions: | ||||||
|  | 			return SUCCESS | ||||||
|  | 
 | ||||||
|  | 		return RUNNING | ||||||
|  | 	else: | ||||||
|  | 		return SUCCESS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"LimiterDecorator") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										35
									
								
								addons/beehave/nodes/decorators/succeeder.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								addons/beehave/nodes/decorators/succeeder.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/succeeder.svg") | ||||||
|  | class_name AlwaysSucceedDecorator extends Decorator | ||||||
|  | 
 | ||||||
|  | ## A succeeder node will always return a `SUCCESS` status code. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var c = get_child(0) | ||||||
|  | 
 | ||||||
|  | 	if c != running_child: | ||||||
|  | 		c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 	var response = c.tick(actor, blackboard) | ||||||
|  | 	if can_send_message(blackboard): | ||||||
|  | 		BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 	if c is ConditionLeaf: | ||||||
|  | 		blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 		blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 	if response == RUNNING: | ||||||
|  | 		running_child = c | ||||||
|  | 		if c is ActionLeaf: | ||||||
|  | 			blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 		return RUNNING | ||||||
|  | 	else: | ||||||
|  | 		c.after_run(actor, blackboard) | ||||||
|  | 		return SUCCESS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"AlwaysSucceedDecorator") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										60
									
								
								addons/beehave/nodes/decorators/time_limiter.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								addons/beehave/nodes/decorators/time_limiter.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/limiter.svg") | ||||||
|  | class_name TimeLimiterDecorator extends Decorator | ||||||
|  | 
 | ||||||
|  | ## The Time Limit Decorator will give its `RUNNING` child a set amount of time to finish | ||||||
|  | ## before interrupting it and return a `FAILURE` status code. | ||||||
|  | ## The timer resets the next time that a child is not `RUNNING` | ||||||
|  | 
 | ||||||
|  | @export var wait_time := 0.0 | ||||||
|  | 
 | ||||||
|  | @onready var cache_key = "time_limiter_%s" % self.get_instance_id() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	if not get_child_count() == 1: | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 	var child = self.get_child(0) | ||||||
|  | 	var time_left = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 	if time_left < wait_time: | ||||||
|  | 		time_left += get_physics_process_delta_time() | ||||||
|  | 		blackboard.set_value(cache_key, time_left, str(actor.get_instance_id())) | ||||||
|  | 		var response = child.tick(actor, blackboard) | ||||||
|  | 		if can_send_message(blackboard): | ||||||
|  | 			BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 		if child is ConditionLeaf: | ||||||
|  | 			blackboard.set_value("last_condition", child, str(actor.get_instance_id())) | ||||||
|  | 			blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 		if response == RUNNING: | ||||||
|  | 			running_child = child | ||||||
|  | 			if child is ActionLeaf: | ||||||
|  | 				blackboard.set_value("running_action", child, str(actor.get_instance_id())) | ||||||
|  | 		else: | ||||||
|  | 			child.after_run(actor, blackboard) | ||||||
|  | 		return response | ||||||
|  | 	else: | ||||||
|  | 		interrupt(actor, blackboard) | ||||||
|  | 		child.after_run(actor, blackboard) | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func before_run(actor: Node, blackboard: Blackboard) -> void: | ||||||
|  | 	blackboard.set_value(cache_key, 0.0, str(actor.get_instance_id())) | ||||||
|  | 	if get_child_count() > 0: | ||||||
|  | 		get_child(0).before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"TimeLimiterDecorator") | ||||||
|  | 	return classes | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_configuration_warnings() -> PackedStringArray: | ||||||
|  | 	if not get_child_count() == 1: | ||||||
|  | 		return ["Requires exactly one child node"] | ||||||
|  | 	return [] | ||||||
							
								
								
									
										33
									
								
								addons/beehave/nodes/decorators/until_fail.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								addons/beehave/nodes/decorators/until_fail.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/until_fail.svg") | ||||||
|  | class_name UntilFailDecorator | ||||||
|  | extends Decorator | ||||||
|  | 
 | ||||||
|  | ## The UntilFail Decorator will return `RUNNING` if its child returns | ||||||
|  | ## `SUCCESS` or `RUNNING` or it will return `SUCCESS` if its child returns | ||||||
|  | ## `FAILURE` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var c = get_child(0) | ||||||
|  | 
 | ||||||
|  | 	if c != running_child: | ||||||
|  | 		c.before_run(actor, blackboard) | ||||||
|  | 
 | ||||||
|  | 	var response = c.tick(actor, blackboard) | ||||||
|  | 	if can_send_message(blackboard): | ||||||
|  | 		BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) | ||||||
|  | 
 | ||||||
|  | 	if c is ConditionLeaf: | ||||||
|  | 		blackboard.set_value("last_condition", c, str(actor.get_instance_id())) | ||||||
|  | 		blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) | ||||||
|  | 
 | ||||||
|  | 	if response == RUNNING: | ||||||
|  | 		running_child = c | ||||||
|  | 		if c is ActionLeaf: | ||||||
|  | 			blackboard.set_value("running_action", c, str(actor.get_instance_id())) | ||||||
|  | 		return RUNNING | ||||||
|  | 	if response == SUCCESS: | ||||||
|  | 		return RUNNING | ||||||
|  | 
 | ||||||
|  | 	return SUCCESS | ||||||
							
								
								
									
										14
									
								
								addons/beehave/nodes/leaves/action.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								addons/beehave/nodes/leaves/action.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/action.svg") | ||||||
|  | class_name ActionLeaf extends Leaf | ||||||
|  | 
 | ||||||
|  | ## Actions are leaf nodes that define a task to be performed by an actor. | ||||||
|  | ## Their execution can be long running, potentially being called across multiple | ||||||
|  | ## frame executions. In this case, the node should return `RUNNING` until the | ||||||
|  | ## action is completed. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"ActionLeaf") | ||||||
|  | 	return classes | ||||||
							
								
								
									
										65
									
								
								addons/beehave/nodes/leaves/blackboard_compare.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								addons/beehave/nodes/leaves/blackboard_compare.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | |||||||
|  | @tool | ||||||
|  | class_name BlackboardCompareCondition extends ConditionLeaf | ||||||
|  | 
 | ||||||
|  | ## Compares two values using the specified comparison operator. | ||||||
|  | ## Returns [code]FAILURE[/code] if any of the expression fails or the | ||||||
|  | ## comparison operation returns [code]false[/code], otherwise it returns [code]SUCCESS[/code]. | ||||||
|  | 
 | ||||||
|  | enum Operators { | ||||||
|  | 	EQUAL, | ||||||
|  | 	NOT_EQUAL, | ||||||
|  | 	GREATER, | ||||||
|  | 	LESS, | ||||||
|  | 	GREATER_EQUAL, | ||||||
|  | 	LESS_EQUAL, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ## Expression represetning left operand. | ||||||
|  | ## This value can be any valid GDScript expression. | ||||||
|  | ## In order to use the existing blackboard keys for comparison, | ||||||
|  | ## use get_value("key_name") e.g. get_value("direction").length() | ||||||
|  | @export_placeholder(EXPRESSION_PLACEHOLDER) var left_operand: String = "" | ||||||
|  | ## Comparison operator. | ||||||
|  | @export_enum("==", "!=", ">", "<", ">=", "<=") var operator: int = 0 | ||||||
|  | ## Expression represetning right operand. | ||||||
|  | ## This value can be any valid GDScript expression. | ||||||
|  | ## In order to use the existing blackboard keys for comparison, | ||||||
|  | ## use get_value("key_name") e.g. get_value("direction").length() | ||||||
|  | @export_placeholder(EXPRESSION_PLACEHOLDER) var right_operand: String = "" | ||||||
|  | 
 | ||||||
|  | @onready var _left_expression: Expression = _parse_expression(left_operand) | ||||||
|  | @onready var _right_expression: Expression = _parse_expression(right_operand) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var left: Variant = _left_expression.execute([], blackboard) | ||||||
|  | 
 | ||||||
|  | 	if _left_expression.has_execute_failed(): | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 	var right: Variant = _right_expression.execute([], blackboard) | ||||||
|  | 
 | ||||||
|  | 	if _right_expression.has_execute_failed(): | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 	var result: bool = false | ||||||
|  | 
 | ||||||
|  | 	match operator: | ||||||
|  | 		Operators.EQUAL: | ||||||
|  | 			result = left == right | ||||||
|  | 		Operators.NOT_EQUAL: | ||||||
|  | 			result = left != right | ||||||
|  | 		Operators.GREATER: | ||||||
|  | 			result = left > right | ||||||
|  | 		Operators.LESS: | ||||||
|  | 			result = left < right | ||||||
|  | 		Operators.GREATER_EQUAL: | ||||||
|  | 			result = left >= right | ||||||
|  | 		Operators.LESS_EQUAL: | ||||||
|  | 			result = left <= right | ||||||
|  | 
 | ||||||
|  | 	return SUCCESS if result else FAILURE | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_expression_sources() -> Array[String]: | ||||||
|  | 	return [left_operand, right_operand] | ||||||
							
								
								
									
										25
									
								
								addons/beehave/nodes/leaves/blackboard_erase.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								addons/beehave/nodes/leaves/blackboard_erase.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | @tool | ||||||
|  | class_name BlackboardEraseAction extends ActionLeaf | ||||||
|  | 
 | ||||||
|  | ## Erases the specified key from the blackboard. | ||||||
|  | ## Returns [code]FAILURE[/code] if expression execution fails, otherwise [code]SUCCESS[/code]. | ||||||
|  | 
 | ||||||
|  | ## Expression representing a blackboard key. | ||||||
|  | @export_placeholder(EXPRESSION_PLACEHOLDER) var key: String = "" | ||||||
|  | 
 | ||||||
|  | @onready var _key_expression: Expression = _parse_expression(key) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var key_value: Variant = _key_expression.execute([], blackboard) | ||||||
|  | 
 | ||||||
|  | 	if _key_expression.has_execute_failed(): | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 	blackboard.erase_value(key_value) | ||||||
|  | 
 | ||||||
|  | 	return SUCCESS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_expression_sources() -> Array[String]: | ||||||
|  | 	return [key] | ||||||
							
								
								
									
										23
									
								
								addons/beehave/nodes/leaves/blackboard_has.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								addons/beehave/nodes/leaves/blackboard_has.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | @tool | ||||||
|  | class_name BlackboardHasCondition extends ConditionLeaf | ||||||
|  | 
 | ||||||
|  | ## Returns [code]FAILURE[/code] if expression execution fails or the specified key doesn't exist. | ||||||
|  | ## Returns [code]SUCCESS[/code] if blackboard has the specified key. | ||||||
|  | 
 | ||||||
|  | ## Expression representing a blackboard key. | ||||||
|  | @export_placeholder(EXPRESSION_PLACEHOLDER) var key: String = "" | ||||||
|  | 
 | ||||||
|  | @onready var _key_expression: Expression = _parse_expression(key) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var key_value: Variant = _key_expression.execute([], blackboard) | ||||||
|  | 
 | ||||||
|  | 	if _key_expression.has_execute_failed(): | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 	return SUCCESS if blackboard.has_value(key_value) else FAILURE | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_expression_sources() -> Array[String]: | ||||||
|  | 	return [key] | ||||||
							
								
								
									
										33
									
								
								addons/beehave/nodes/leaves/blackboard_set.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								addons/beehave/nodes/leaves/blackboard_set.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | @tool | ||||||
|  | class_name BlackboardSetAction extends ActionLeaf | ||||||
|  | 
 | ||||||
|  | ## Sets the specified key to the specified value. | ||||||
|  | ## Returns [code]FAILURE[/code] if expression execution fails, otherwise [code]SUCCESS[/code]. | ||||||
|  | 
 | ||||||
|  | ## Expression representing a blackboard key. | ||||||
|  | @export_placeholder(EXPRESSION_PLACEHOLDER) var key: String = "" | ||||||
|  | ## Expression representing a blackboard value to assign to the specified key. | ||||||
|  | @export_placeholder(EXPRESSION_PLACEHOLDER) var value: String = "" | ||||||
|  | 
 | ||||||
|  | @onready var _key_expression: Expression = _parse_expression(key) | ||||||
|  | @onready var _value_expression: Expression = _parse_expression(value) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func tick(actor: Node, blackboard: Blackboard) -> int: | ||||||
|  | 	var key_value: Variant = _key_expression.execute([], blackboard) | ||||||
|  | 
 | ||||||
|  | 	if _key_expression.has_execute_failed(): | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 	var value_value: Variant = _value_expression.execute([], blackboard) | ||||||
|  | 
 | ||||||
|  | 	if _value_expression.has_execute_failed(): | ||||||
|  | 		return FAILURE | ||||||
|  | 
 | ||||||
|  | 	blackboard.set_value(key_value, value_value) | ||||||
|  | 
 | ||||||
|  | 	return SUCCESS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _get_expression_sources() -> Array[String]: | ||||||
|  | 	return [key, value] | ||||||
							
								
								
									
										12
									
								
								addons/beehave/nodes/leaves/condition.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								addons/beehave/nodes/leaves/condition.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | @tool | ||||||
|  | @icon("../../icons/condition.svg") | ||||||
|  | class_name ConditionLeaf extends Leaf | ||||||
|  | 
 | ||||||
|  | ## Conditions are leaf nodes that either return SUCCESS or FAILURE depending on | ||||||
|  | ## a single simple condition. They should never return `RUNNING`. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func get_class_name() -> Array[StringName]: | ||||||
|  | 	var classes := super() | ||||||
|  | 	classes.push_back(&"ConditionLeaf") | ||||||
|  | 	return classes | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user