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