diff --git a/_daily_basis/report_2026-01-03.md b/_daily_basis/report_2026-01-03.md new file mode 100644 index 0000000..f22c0b4 --- /dev/null +++ b/_daily_basis/report_2026-01-03.md @@ -0,0 +1,21 @@ +[ ADT's Report ] + +Updated the `tekton-enet` ( Armageddon Multiplayer ) on branch `launcher` + +**Bot System Refactor** + +✅ **Standalone Bot Controller** - Replaced the heavy `Beehave` dependency with a procedural, lightweight `BotController.gd`. This makes bot logic modular, easier to debug, and strictly separates it from human player logic. + +✅ **Bot Identity Fixes** - Fixed an issue where Bots would overwrite their unique IDs with the Server ID (1), causing them to mimic the Host. Bots now correctly maintain their own identities (Bot 2, Bot 3, etc.) and names ("Bot", "Alpha", "Beta"...). + +✅ **Idle Bot Fix** - Resolved an issue where bots would stop moving after 2 actions in real-time mode. Bots now ignore Action Point limits when `TurnManager.turn_based_mode` is disabled, ensuring continuous gameplay. + +✅ **Host UI Protection** - Implemented robust guards in `player.gd` and `player_action_manager.gd` to prevent Bot actions (which run on the Host) from updating the Host's personal UI (Playerboard, Highlights). The Host's screen is now clean. + +✅ **Memory Optimization** - Added explicit `_exit_tree()` cleanups and `is_instance_valid()` guards to `BotController`. This ensures bots don't leak memory or crash if the game is restarted while they are performing an action. + +**Visual & Pathfinding** + +✅ **Movement Fixes** - Bots now correctly use the `EnhancedGridMap` pathfinding to navigate around obstacles and reach goal tiles. + +✅ **Character Determinism** - Bots now predictably select character models based on their ID, matching the Lobby preview and ensuring distinct visuals on the race track. diff --git a/addons/beehave/LICENSE b/addons/beehave/LICENSE deleted file mode 100644 index caabbff..0000000 --- a/addons/beehave/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -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. diff --git a/addons/beehave/blackboard.gd b/addons/beehave/blackboard.gd deleted file mode 100644 index e4ad82b..0000000 --- a/addons/beehave/blackboard.gd +++ /dev/null @@ -1,54 +0,0 @@ -@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 - -func get_debug_data() -> Dictionary: - return _data diff --git a/addons/beehave/blackboard.gd.uid b/addons/beehave/blackboard.gd.uid deleted file mode 100644 index ce88a9e..0000000 --- a/addons/beehave/blackboard.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dtev3hcobhh3v diff --git a/addons/beehave/debug/debugger.gd b/addons/beehave/debug/debugger.gd deleted file mode 100644 index a41e072..0000000 --- a/addons/beehave/debug/debugger.gd +++ /dev/null @@ -1,96 +0,0 @@ -@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], data[2]) - return true - if message == "beehave:process_begin": - debugger_tab.graph.process_begin(data[0], data[1]) - return true - if message == "beehave:process_end": - debugger_tab.graph.process_end(data[0], data[1]) - 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 diff --git a/addons/beehave/debug/debugger.gd.uid b/addons/beehave/debug/debugger.gd.uid deleted file mode 100644 index 8037039..0000000 --- a/addons/beehave/debug/debugger.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c4f1tr8nox531 diff --git a/addons/beehave/debug/debugger_messages.gd b/addons/beehave/debug/debugger_messages.gd deleted file mode 100644 index caae409..0000000 --- a/addons/beehave/debug/debugger_messages.gd +++ /dev/null @@ -1,30 +0,0 @@ -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, blackboard: Dictionary = {}) -> void: - if can_send_message(): - EngineDebugger.send_message("beehave:process_tick", [instance_id, status, blackboard]) - - -static func process_begin(instance_id: int, blackboard: Dictionary = {}) -> void: - if can_send_message(): - EngineDebugger.send_message("beehave:process_begin", [instance_id, blackboard]) - - -static func process_end(instance_id: int, blackboard: Dictionary = {}) -> void: - if can_send_message(): - EngineDebugger.send_message("beehave:process_end", [instance_id, blackboard]) diff --git a/addons/beehave/debug/debugger_messages.gd.uid b/addons/beehave/debug/debugger_messages.gd.uid deleted file mode 100644 index ccb129d..0000000 --- a/addons/beehave/debug/debugger_messages.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://7gg8su7l7adr diff --git a/addons/beehave/debug/debugger_tab.gd b/addons/beehave/debug/debugger_tab.gd deleted file mode 100644 index ec23d29..0000000 --- a/addons/beehave/debug/debugger_tab.gd +++ /dev/null @@ -1,157 +0,0 @@ -@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 NewNodeBlackBoard := preload("new_node_blackboard.gd") - -const TREE_ICON := preload("../icons/tree.svg") - -var graph -var container: HSplitContainer -var graph_container: HSplitContainer -var item_list: ItemList -var blackboard_vbox: VBoxContainer -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) - - graph_container = HSplitContainer.new() - graph_container.split_offset = 1920 - graph_container.set_anchors_preset(Control.PRESET_FULL_RECT) - container.add_child(graph_container) - - if Engine.get_version_info().minor >= 2: - graph = NewBeehaveGraphEdit.new(BeehaveUtils.get_frames()) - else: - graph = OldBeehaveGraphEdit.new(BeehaveUtils.get_frames()) - - graph.node_selected.connect(_on_graph_node_selected) - graph.node_deselected.connect(_on_graph_node_deselected) - graph_container.add_child(graph) - - blackboard_vbox = VBoxContainer.new() - blackboard_vbox.custom_minimum_size = Vector2(200, 0) - blackboard_vbox.set_anchors_preset(Control.PRESET_FULL_RECT) - graph_container.add_child(blackboard_vbox) - - 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, {}) - - # Clear our any loaded blackboards - for child in blackboard_vbox.get_children(): - child.free() - - active_tree_id = id.to_int() - if session != null: - session.send_message("beehave:activate_tree", [active_tree_id]) - -func _on_graph_node_selected(node: GraphNode) -> void: - var node_blackboard: VBoxContainer = NewNodeBlackBoard.new(BeehaveUtils.get_frames(), node) - blackboard_vbox.add_child(node_blackboard) - -func _on_graph_node_deselected(node: GraphNode) -> void: - var matches: Array = blackboard_vbox\ - .get_children()\ - .filter(func (child): return child.name == node.name) - - for child in matches: - child.free() - - -func _on_visibility_changed() -> void: - if session != null: - session.send_message("beehave:visibility_changed", [visible and is_visible_in_tree()]) diff --git a/addons/beehave/debug/debugger_tab.gd.uid b/addons/beehave/debug/debugger_tab.gd.uid deleted file mode 100644 index c5a9541..0000000 --- a/addons/beehave/debug/debugger_tab.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b4dd5cgag3gmj diff --git a/addons/beehave/debug/global_debugger.gd b/addons/beehave/debug/global_debugger.gd deleted file mode 100644 index 7350b3e..0000000 --- a/addons/beehave/debug/global_debugger.gd +++ /dev/null @@ -1,38 +0,0 @@ -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()) diff --git a/addons/beehave/debug/global_debugger.gd.uid b/addons/beehave/debug/global_debugger.gd.uid deleted file mode 100644 index 618b265..0000000 --- a/addons/beehave/debug/global_debugger.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dsq1f7h88mp4m diff --git a/addons/beehave/debug/icons/horizontal_layout.svg b/addons/beehave/debug/icons/horizontal_layout.svg deleted file mode 100644 index 235dc62..0000000 --- a/addons/beehave/debug/icons/horizontal_layout.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/beehave/debug/icons/horizontal_layout.svg.import b/addons/beehave/debug/icons/horizontal_layout.svg.import deleted file mode 100644 index 539e518..0000000 --- a/addons/beehave/debug/icons/horizontal_layout.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/debug/icons/port_bottom.svg b/addons/beehave/debug/icons/port_bottom.svg deleted file mode 100644 index 3ce70e7..0000000 --- a/addons/beehave/debug/icons/port_bottom.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/beehave/debug/icons/port_bottom.svg.import b/addons/beehave/debug/icons/port_bottom.svg.import deleted file mode 100644 index 8845c5b..0000000 --- a/addons/beehave/debug/icons/port_bottom.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/debug/icons/port_left.svg b/addons/beehave/debug/icons/port_left.svg deleted file mode 100644 index c1f6717..0000000 --- a/addons/beehave/debug/icons/port_left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/beehave/debug/icons/port_left.svg.import b/addons/beehave/debug/icons/port_left.svg.import deleted file mode 100644 index 7ea9827..0000000 --- a/addons/beehave/debug/icons/port_left.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/debug/icons/port_right.svg b/addons/beehave/debug/icons/port_right.svg deleted file mode 100644 index 2560af5..0000000 --- a/addons/beehave/debug/icons/port_right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/beehave/debug/icons/port_right.svg.import b/addons/beehave/debug/icons/port_right.svg.import deleted file mode 100644 index 20931cd..0000000 --- a/addons/beehave/debug/icons/port_right.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/debug/icons/port_top.svg b/addons/beehave/debug/icons/port_top.svg deleted file mode 100644 index d3b99e1..0000000 --- a/addons/beehave/debug/icons/port_top.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/beehave/debug/icons/port_top.svg.import b/addons/beehave/debug/icons/port_top.svg.import deleted file mode 100644 index dec7820..0000000 --- a/addons/beehave/debug/icons/port_top.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/debug/icons/vertical_layout.svg b/addons/beehave/debug/icons/vertical_layout.svg deleted file mode 100644 index d59ffb0..0000000 --- a/addons/beehave/debug/icons/vertical_layout.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/beehave/debug/icons/vertical_layout.svg.import b/addons/beehave/debug/icons/vertical_layout.svg.import deleted file mode 100644 index 8ddcfca..0000000 --- a/addons/beehave/debug/icons/vertical_layout.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/debug/new_frames.gd b/addons/beehave/debug/new_frames.gd deleted file mode 100644 index 4b739fd..0000000 --- a/addons/beehave/debug/new_frames.gd +++ /dev/null @@ -1,69 +0,0 @@ -@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 diff --git a/addons/beehave/debug/new_frames.gd.uid b/addons/beehave/debug/new_frames.gd.uid deleted file mode 100644 index 9b6c059..0000000 --- a/addons/beehave/debug/new_frames.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cr6a7f0fr1ik7 diff --git a/addons/beehave/debug/new_graph_edit.gd b/addons/beehave/debug/new_graph_edit.gd deleted file mode 100644 index 8e0c607..0000000 --- a/addons/beehave/debug/new_graph_edit.gd +++ /dev/null @@ -1,297 +0,0 @@ -@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, blackboard = null) -> 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, blackboard = null) -> 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) - node.blackboard = blackboard - if status == BeehaveNode.SUCCESS or status == BeehaveNode.RUNNING: - if not active_nodes.has(node.name): - active_nodes.push_back(node.name) - - -func process_end(instance_id: int, blackboard = null) -> void: - if not _is_same_tree(instance_id): - return - - for child in _get_child_nodes(): - var status := child.get_meta("status", -1) - match status: - BeehaveNode.SUCCESS: - active_nodes.erase(child.name) - child.set_color(SUCCESS_COLOR) - BeehaveNode.FAILURE: - active_nodes.erase(child.name) - child.set_color(INACTIVE_COLOR) - BeehaveNode.RUNNING: - 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" diff --git a/addons/beehave/debug/new_graph_edit.gd.uid b/addons/beehave/debug/new_graph_edit.gd.uid deleted file mode 100644 index 592d700..0000000 --- a/addons/beehave/debug/new_graph_edit.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cw7vqce212j2l diff --git a/addons/beehave/debug/new_graph_node.gd b/addons/beehave/debug/new_graph_node.gd deleted file mode 100644 index dbef42b..0000000 --- a/addons/beehave/debug/new_graph_node.gd +++ /dev/null @@ -1,192 +0,0 @@ -@tool -extends GraphNode - -signal blackboard_updated - -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 - -@export var blackboard: Dictionary: - set(value): - blackboard = value - blackboard_updated.emit() - -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 -var panels_tween: Tween - - -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: - BeehaveNode.SUCCESS: _set_stylebox_overrides(frames.panel_success, frames.titlebar_success) - BeehaveNode.FAILURE: _set_stylebox_overrides(frames.panel_failure, frames.titlebar_failure) - BeehaveNode.RUNNING: _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: - # First update and any status change gets immediate panel update - if not has_theme_stylebox_override("panel") or panel_stylebox != frames.panel_normal: - if panels_tween: - panels_tween.kill() - panels_tween = null - - add_theme_stylebox_override("panel", panel_stylebox) - add_theme_stylebox_override("titlebar", titlebar_stylebox) - return - - # Don't need to do anything if we're already tweening back to normal - if panels_tween: - return - - # Don't need to do anything if our colors are already the same as a normal - var cur_panel_stylebox: StyleBox = get_theme_stylebox("panel") - var cur_titlebar_stylebox: StyleBox = get_theme_stylebox("titlebar") - if cur_panel_stylebox.bg_color == frames.panel_normal.bg_color: - return - - # Apply a duplicate of our current panels that we can tween - add_theme_stylebox_override("panel", cur_panel_stylebox.duplicate()) - add_theme_stylebox_override("titlebar", cur_titlebar_stylebox.duplicate()) - cur_panel_stylebox = get_theme_stylebox("panel") - cur_titlebar_stylebox = get_theme_stylebox("titlebar") - - # Going back to normal is a fade - panels_tween = create_tween() - panels_tween.parallel().tween_property(cur_panel_stylebox, "bg_color", panel_stylebox.bg_color, 1.0) - panels_tween.parallel().tween_property(cur_panel_stylebox, "border_color", panel_stylebox.border_color, 1.0) - panels_tween.parallel().tween_property(cur_titlebar_stylebox, "bg_color", panel_stylebox.bg_color, 1.0) - panels_tween.parallel().tween_property(cur_titlebar_stylebox, "border_color", panel_stylebox.border_color, 1.0) - - -func _on_size_changed(): - add_theme_constant_override("port_offset", 12 * BeehaveUtils.get_editor_scale() if horizontal else round(size.x)) diff --git a/addons/beehave/debug/new_graph_node.gd.uid b/addons/beehave/debug/new_graph_node.gd.uid deleted file mode 100644 index ecf23c1..0000000 --- a/addons/beehave/debug/new_graph_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c88jpe7sk8ood diff --git a/addons/beehave/debug/new_node_blackboard.gd b/addons/beehave/debug/new_node_blackboard.gd deleted file mode 100644 index 75c533f..0000000 --- a/addons/beehave/debug/new_node_blackboard.gd +++ /dev/null @@ -1,76 +0,0 @@ -extends VBoxContainer - -var frames: RefCounted -var graph_node: GraphNode - -var item_tree: Tree - -func _init(frames: RefCounted, node: GraphNode) -> void: - self.frames = frames - graph_node = node - - graph_node.blackboard_updated.connect(_update_list) - -func _ready() -> void: - name = graph_node.name - - set_anchors_preset(Control.PRESET_FULL_RECT) - - var title_panel: Panel = Panel.new() - title_panel.set_anchors_preset(Control.PRESET_FULL_RECT) - title_panel.custom_minimum_size = Vector2(200, 50) - add_child(title_panel) - var title_hbox: HBoxContainer = HBoxContainer.new() - title_hbox.alignment = BoxContainer.ALIGNMENT_CENTER - title_hbox.set_anchors_preset(Control.PRESET_FULL_RECT) - title_panel.add_child(title_hbox) - - var icon_rect: TextureRect = TextureRect.new() - icon_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED - icon_rect.texture = graph_node.icon - icon_rect.set_size(Vector2(20, 20)) - title_hbox.add_child(icon_rect) - - var title: Label = Label.new() - title.text = graph_node.title_text - title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - title.set_anchors_preset(Control.PRESET_FULL_RECT) - title_hbox.add_child(title) - - item_tree = Tree.new() - item_tree.custom_minimum_size = Vector2(200, 400) - item_tree.hide_root = true - item_tree.allow_search = false - item_tree.columns = 2 - add_child(item_tree) - - _update_list() - -func _update_list() -> void: - item_tree.clear() - - var root: TreeItem = item_tree.create_item() - - if graph_node.blackboard.size() == 0: - var no_bb_message: TreeItem = item_tree.create_item(root) - no_bb_message.set_text(0, "No blackboard data") - return - - for bb_name in graph_node.blackboard: - var bb_name_branch: TreeItem = item_tree.create_item(root) - bb_name_branch.set_text(0, bb_name) - - #print(graph_node.blackboard.get(bb_name)) - for key in graph_node.blackboard.get(bb_name): - var bb_kv_leaf: TreeItem = item_tree.create_item(bb_name_branch) - bb_kv_leaf.set_text(0, str(key)) - bb_kv_leaf.set_text(1, str(graph_node.blackboard.get(bb_name).get(key))) - -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 diff --git a/addons/beehave/debug/new_node_blackboard.gd.uid b/addons/beehave/debug/new_node_blackboard.gd.uid deleted file mode 100644 index f8e838e..0000000 --- a/addons/beehave/debug/new_node_blackboard.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bqny8r14mh7ut diff --git a/addons/beehave/debug/old_frames.gd b/addons/beehave/debug/old_frames.gd deleted file mode 100644 index a9f6aa8..0000000 --- a/addons/beehave/debug/old_frames.gd +++ /dev/null @@ -1,47 +0,0 @@ -@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 diff --git a/addons/beehave/debug/old_frames.gd.uid b/addons/beehave/debug/old_frames.gd.uid deleted file mode 100644 index b603ba4..0000000 --- a/addons/beehave/debug/old_frames.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://7o2r5guwuf60 diff --git a/addons/beehave/debug/old_graph_edit.gd b/addons/beehave/debug/old_graph_edit.gd deleted file mode 100644 index 69c11a3..0000000 --- a/addons/beehave/debug/old_graph_edit.gd +++ /dev/null @@ -1,286 +0,0 @@ -@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 == BeehaveNode.SUCCESS or status == BeehaveNode.RUNNING: - 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: - BeehaveNode.SUCCESS: - active_nodes.erase(child.name) - child.set_color(SUCCESS_COLOR) - BeehaveNode.FAILURE: - active_nodes.erase(child.name) - child.set_color(INACTIVE_COLOR) - BeehaveNode.RUNNING: - 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" - ) diff --git a/addons/beehave/debug/old_graph_edit.gd.uid b/addons/beehave/debug/old_graph_edit.gd.uid deleted file mode 100644 index e193128..0000000 --- a/addons/beehave/debug/old_graph_edit.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://con6dkacbux3 diff --git a/addons/beehave/debug/old_graph_node.gd b/addons/beehave/debug/old_graph_node.gd deleted file mode 100644 index 28503b1..0000000 --- a/addons/beehave/debug/old_graph_node.gd +++ /dev/null @@ -1,166 +0,0 @@ -@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: - BeehaveNode.SUCCESS: - return frames.success - BeehaveNode.FAILURE: - return frames.failure - BeehaveNode.RUNNING: - 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) - ) diff --git a/addons/beehave/debug/old_graph_node.gd.uid b/addons/beehave/debug/old_graph_node.gd.uid deleted file mode 100644 index 39c76cd..0000000 --- a/addons/beehave/debug/old_graph_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b4l2tfd0asx38 diff --git a/addons/beehave/debug/tree_node.gd b/addons/beehave/debug/tree_node.gd deleted file mode 100644 index 1377970..0000000 --- a/addons/beehave/debug/tree_node.gd +++ /dev/null @@ -1,275 +0,0 @@ -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()) diff --git a/addons/beehave/debug/tree_node.gd.uid b/addons/beehave/debug/tree_node.gd.uid deleted file mode 100644 index cfae741..0000000 --- a/addons/beehave/debug/tree_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cq6m4hib5jnje diff --git a/addons/beehave/icons/action.svg b/addons/beehave/icons/action.svg deleted file mode 100644 index 3916c89..0000000 --- a/addons/beehave/icons/action.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/action.svg.import b/addons/beehave/icons/action.svg.import deleted file mode 100644 index cf8a612..0000000 --- a/addons/beehave/icons/action.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/blackboard.svg b/addons/beehave/icons/blackboard.svg deleted file mode 100644 index e4948a5..0000000 --- a/addons/beehave/icons/blackboard.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/blackboard.svg.import b/addons/beehave/icons/blackboard.svg.import deleted file mode 100644 index 4650058..0000000 --- a/addons/beehave/icons/blackboard.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/category_bt.svg b/addons/beehave/icons/category_bt.svg deleted file mode 100644 index 8be61ae..0000000 --- a/addons/beehave/icons/category_bt.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/category_bt.svg.import b/addons/beehave/icons/category_bt.svg.import deleted file mode 100644 index c11e4f2..0000000 --- a/addons/beehave/icons/category_bt.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/category_composite.svg b/addons/beehave/icons/category_composite.svg deleted file mode 100644 index aa8b866..0000000 --- a/addons/beehave/icons/category_composite.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/category_composite.svg.import b/addons/beehave/icons/category_composite.svg.import deleted file mode 100644 index 0496273..0000000 --- a/addons/beehave/icons/category_composite.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/category_decorator.svg b/addons/beehave/icons/category_decorator.svg deleted file mode 100644 index 165e3d6..0000000 --- a/addons/beehave/icons/category_decorator.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/category_decorator.svg.import b/addons/beehave/icons/category_decorator.svg.import deleted file mode 100644 index 492f32e..0000000 --- a/addons/beehave/icons/category_decorator.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/category_leaf.svg b/addons/beehave/icons/category_leaf.svg deleted file mode 100644 index 1482fe6..0000000 --- a/addons/beehave/icons/category_leaf.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/category_leaf.svg.import b/addons/beehave/icons/category_leaf.svg.import deleted file mode 100644 index 4ef9604..0000000 --- a/addons/beehave/icons/category_leaf.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/condition.svg b/addons/beehave/icons/condition.svg deleted file mode 100644 index 37b2c7a..0000000 --- a/addons/beehave/icons/condition.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/condition.svg.import b/addons/beehave/icons/condition.svg.import deleted file mode 100644 index ef59099..0000000 --- a/addons/beehave/icons/condition.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/cooldown.svg b/addons/beehave/icons/cooldown.svg deleted file mode 100644 index fbdfd6a..0000000 --- a/addons/beehave/icons/cooldown.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/cooldown.svg.import b/addons/beehave/icons/cooldown.svg.import deleted file mode 100644 index 872997e..0000000 --- a/addons/beehave/icons/cooldown.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://caxqtxa8nrt3b" -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 diff --git a/addons/beehave/icons/delayer.svg b/addons/beehave/icons/delayer.svg deleted file mode 100644 index 21cb617..0000000 --- a/addons/beehave/icons/delayer.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/delayer.svg.import b/addons/beehave/icons/delayer.svg.import deleted file mode 100644 index 05c0269..0000000 --- a/addons/beehave/icons/delayer.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://do678vjitrp7g" -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 diff --git a/addons/beehave/icons/failer.svg b/addons/beehave/icons/failer.svg deleted file mode 100644 index 968f7e1..0000000 --- a/addons/beehave/icons/failer.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/failer.svg.import b/addons/beehave/icons/failer.svg.import deleted file mode 100644 index 989b556..0000000 --- a/addons/beehave/icons/failer.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/inverter.svg b/addons/beehave/icons/inverter.svg deleted file mode 100644 index d4e791e..0000000 --- a/addons/beehave/icons/inverter.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/inverter.svg.import b/addons/beehave/icons/inverter.svg.import deleted file mode 100644 index e9050a8..0000000 --- a/addons/beehave/icons/inverter.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/limiter.svg b/addons/beehave/icons/limiter.svg deleted file mode 100644 index 7b3fa1d..0000000 --- a/addons/beehave/icons/limiter.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/limiter.svg.import b/addons/beehave/icons/limiter.svg.import deleted file mode 100644 index 7b56b08..0000000 --- a/addons/beehave/icons/limiter.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/repeater.svg b/addons/beehave/icons/repeater.svg deleted file mode 100644 index 47c46e9..0000000 --- a/addons/beehave/icons/repeater.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - diff --git a/addons/beehave/icons/repeater.svg.import b/addons/beehave/icons/repeater.svg.import deleted file mode 100644 index b356afd..0000000 --- a/addons/beehave/icons/repeater.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dyu3n86f8rhto" -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 diff --git a/addons/beehave/icons/selector.svg b/addons/beehave/icons/selector.svg deleted file mode 100644 index 0ae3b7a..0000000 --- a/addons/beehave/icons/selector.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/selector.svg.import b/addons/beehave/icons/selector.svg.import deleted file mode 100644 index ef7326d..0000000 --- a/addons/beehave/icons/selector.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/selector_random.svg b/addons/beehave/icons/selector_random.svg deleted file mode 100644 index 6f631e9..0000000 --- a/addons/beehave/icons/selector_random.svg +++ /dev/null @@ -1,35 +0,0 @@ - - diff --git a/addons/beehave/icons/selector_random.svg.import b/addons/beehave/icons/selector_random.svg.import deleted file mode 100644 index 6306f76..0000000 --- a/addons/beehave/icons/selector_random.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/selector_reactive.svg b/addons/beehave/icons/selector_reactive.svg deleted file mode 100644 index 6db005f..0000000 --- a/addons/beehave/icons/selector_reactive.svg +++ /dev/null @@ -1,45 +0,0 @@ - - diff --git a/addons/beehave/icons/selector_reactive.svg.import b/addons/beehave/icons/selector_reactive.svg.import deleted file mode 100644 index 12a8c5b..0000000 --- a/addons/beehave/icons/selector_reactive.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/sequence.svg b/addons/beehave/icons/sequence.svg deleted file mode 100644 index 3ebedd9..0000000 --- a/addons/beehave/icons/sequence.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/sequence.svg.import b/addons/beehave/icons/sequence.svg.import deleted file mode 100644 index 5dadbe2..0000000 --- a/addons/beehave/icons/sequence.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/sequence_random.svg b/addons/beehave/icons/sequence_random.svg deleted file mode 100644 index 34e4a12..0000000 --- a/addons/beehave/icons/sequence_random.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/sequence_random.svg.import b/addons/beehave/icons/sequence_random.svg.import deleted file mode 100644 index 3907462..0000000 --- a/addons/beehave/icons/sequence_random.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/sequence_reactive.svg b/addons/beehave/icons/sequence_reactive.svg deleted file mode 100644 index 33d219b..0000000 --- a/addons/beehave/icons/sequence_reactive.svg +++ /dev/null @@ -1,60 +0,0 @@ - - diff --git a/addons/beehave/icons/sequence_reactive.svg.import b/addons/beehave/icons/sequence_reactive.svg.import deleted file mode 100644 index ab0fa25..0000000 --- a/addons/beehave/icons/sequence_reactive.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/simple_parallel.svg b/addons/beehave/icons/simple_parallel.svg deleted file mode 100644 index e9c8b00..0000000 --- a/addons/beehave/icons/simple_parallel.svg +++ /dev/null @@ -1,13 +0,0 @@ - - simple_parallel - - Layer 1 - - - - - - - - - \ No newline at end of file diff --git a/addons/beehave/icons/simple_parallel.svg.import b/addons/beehave/icons/simple_parallel.svg.import deleted file mode 100644 index 83f6e03..0000000 --- a/addons/beehave/icons/simple_parallel.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://byadtv6f4v1ni" -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 diff --git a/addons/beehave/icons/succeeder.svg b/addons/beehave/icons/succeeder.svg deleted file mode 100644 index 10f5912..0000000 --- a/addons/beehave/icons/succeeder.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/succeeder.svg.import b/addons/beehave/icons/succeeder.svg.import deleted file mode 100644 index 0cb7334..0000000 --- a/addons/beehave/icons/succeeder.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/tree.svg b/addons/beehave/icons/tree.svg deleted file mode 100644 index 6c85ea1..0000000 --- a/addons/beehave/icons/tree.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/addons/beehave/icons/tree.svg.import b/addons/beehave/icons/tree.svg.import deleted file mode 100644 index 9ac0308..0000000 --- a/addons/beehave/icons/tree.svg.import +++ /dev/null @@ -1,38 +0,0 @@ -[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 diff --git a/addons/beehave/icons/until_fail.svg b/addons/beehave/icons/until_fail.svg deleted file mode 100644 index c64a0a0..0000000 --- a/addons/beehave/icons/until_fail.svg +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - diff --git a/addons/beehave/icons/until_fail.svg.import b/addons/beehave/icons/until_fail.svg.import deleted file mode 100644 index fb942cb..0000000 --- a/addons/beehave/icons/until_fail.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dbqbww1apa6bb" -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 diff --git a/addons/beehave/metrics/beehave_global_metrics.gd b/addons/beehave/metrics/beehave_global_metrics.gd deleted file mode 100644 index ef29db2..0000000 --- a/addons/beehave/metrics/beehave_global_metrics.gd +++ /dev/null @@ -1,54 +0,0 @@ -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 diff --git a/addons/beehave/metrics/beehave_global_metrics.gd.uid b/addons/beehave/metrics/beehave_global_metrics.gd.uid deleted file mode 100644 index c300a74..0000000 --- a/addons/beehave/metrics/beehave_global_metrics.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ois12pcskdb3 diff --git a/addons/beehave/nodes/beehave_node.gd b/addons/beehave/nodes/beehave_node.gd deleted file mode 100644 index 9ab8d6e..0000000 --- a/addons/beehave/nodes/beehave_node.gd +++ /dev/null @@ -1,46 +0,0 @@ -@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) diff --git a/addons/beehave/nodes/beehave_node.gd.uid b/addons/beehave/nodes/beehave_node.gd.uid deleted file mode 100644 index fd20b92..0000000 --- a/addons/beehave/nodes/beehave_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bg04ak3vbgask diff --git a/addons/beehave/nodes/beehave_tree.gd b/addons/beehave/nodes/beehave_tree.gd deleted file mode 100644 index ecdbe52..0000000 --- a/addons/beehave/nodes/beehave_tree.gd +++ /dev/null @@ -1,329 +0,0 @@ -@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(), blackboard.get_debug_data()) - - if self.get_child_count() == 1: - tick() - - if _can_send_message: - BeehaveDebuggerMessages.process_end(get_instance_id(), blackboard.get_debug_data()) - - # 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, blackboard.get_debug_data()) - BeehaveDebuggerMessages.process_tick(get_instance_id(), status, blackboard.get_debug_data()) - - # 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") diff --git a/addons/beehave/nodes/beehave_tree.gd.uid b/addons/beehave/nodes/beehave_tree.gd.uid deleted file mode 100644 index 3f36972..0000000 --- a/addons/beehave/nodes/beehave_tree.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c5q0j8uujs8k1 diff --git a/addons/beehave/nodes/composites/composite.gd b/addons/beehave/nodes/composites/composite.gd deleted file mode 100644 index 53a8e90..0000000 --- a/addons/beehave/nodes/composites/composite.gd +++ /dev/null @@ -1,34 +0,0 @@ -@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 diff --git a/addons/beehave/nodes/composites/composite.gd.uid b/addons/beehave/nodes/composites/composite.gd.uid deleted file mode 100644 index 6a2eb25..0000000 --- a/addons/beehave/nodes/composites/composite.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://caj56bhuit8y1 diff --git a/addons/beehave/nodes/composites/randomized_composite.gd b/addons/beehave/nodes/composites/randomized_composite.gd deleted file mode 100644 index 2afa5e9..0000000 --- a/addons/beehave/nodes/composites/randomized_composite.gd +++ /dev/null @@ -1,176 +0,0 @@ -@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 diff --git a/addons/beehave/nodes/composites/randomized_composite.gd.uid b/addons/beehave/nodes/composites/randomized_composite.gd.uid deleted file mode 100644 index 1096930..0000000 --- a/addons/beehave/nodes/composites/randomized_composite.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bw88prwmrywoa diff --git a/addons/beehave/nodes/composites/selector.gd b/addons/beehave/nodes/composites/selector.gd deleted file mode 100644 index 69ce1e9..0000000 --- a/addons/beehave/nodes/composites/selector.gd +++ /dev/null @@ -1,69 +0,0 @@ -@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: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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: String = 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 diff --git a/addons/beehave/nodes/composites/selector.gd.uid b/addons/beehave/nodes/composites/selector.gd.uid deleted file mode 100644 index d502e5b..0000000 --- a/addons/beehave/nodes/composites/selector.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dtctf507t71tj diff --git a/addons/beehave/nodes/composites/selector_random.gd b/addons/beehave/nodes/composites/selector_random.gd deleted file mode 100644 index 54ea63e..0000000 --- a/addons/beehave/nodes/composites/selector_random.gd +++ /dev/null @@ -1,82 +0,0 @@ -@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: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/composites/selector_random.gd.uid b/addons/beehave/nodes/composites/selector_random.gd.uid deleted file mode 100644 index aad51f6..0000000 --- a/addons/beehave/nodes/composites/selector_random.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c74iyjawmc3nt diff --git a/addons/beehave/nodes/composites/selector_reactive.gd b/addons/beehave/nodes/composites/selector_reactive.gd deleted file mode 100644 index 1dae759..0000000 --- a/addons/beehave/nodes/composites/selector_reactive.gd +++ /dev/null @@ -1,47 +0,0 @@ -@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: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/composites/selector_reactive.gd.uid b/addons/beehave/nodes/composites/selector_reactive.gd.uid deleted file mode 100644 index cb8048f..0000000 --- a/addons/beehave/nodes/composites/selector_reactive.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://7q4xte38jyq7 diff --git a/addons/beehave/nodes/composites/sequence.gd b/addons/beehave/nodes/composites/sequence.gd deleted file mode 100644 index 4978d93..0000000 --- a/addons/beehave/nodes/composites/sequence.gd +++ /dev/null @@ -1,75 +0,0 @@ -@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: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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: String = 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 diff --git a/addons/beehave/nodes/composites/sequence.gd.uid b/addons/beehave/nodes/composites/sequence.gd.uid deleted file mode 100644 index 3f48eb4..0000000 --- a/addons/beehave/nodes/composites/sequence.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://t6e8rtdiqhg5 diff --git a/addons/beehave/nodes/composites/sequence_random.gd b/addons/beehave/nodes/composites/sequence_random.gd deleted file mode 100644 index 08709ab..0000000 --- a/addons/beehave/nodes/composites/sequence_random.gd +++ /dev/null @@ -1,96 +0,0 @@ -@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: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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: Array[Node] = 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 diff --git a/addons/beehave/nodes/composites/sequence_random.gd.uid b/addons/beehave/nodes/composites/sequence_random.gd.uid deleted file mode 100644 index 6749865..0000000 --- a/addons/beehave/nodes/composites/sequence_random.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b2v7q8n5rsucr diff --git a/addons/beehave/nodes/composites/sequence_reactive.gd b/addons/beehave/nodes/composites/sequence_reactive.gd deleted file mode 100644 index f071181..0000000 --- a/addons/beehave/nodes/composites/sequence_reactive.gd +++ /dev/null @@ -1,63 +0,0 @@ -@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: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/composites/sequence_reactive.gd.uid b/addons/beehave/nodes/composites/sequence_reactive.gd.uid deleted file mode 100644 index 462a243..0000000 --- a/addons/beehave/nodes/composites/sequence_reactive.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bt7kp7k128gw4 diff --git a/addons/beehave/nodes/composites/sequence_star.gd b/addons/beehave/nodes/composites/sequence_star.gd deleted file mode 100644 index c608ca1..0000000 --- a/addons/beehave/nodes/composites/sequence_star.gd +++ /dev/null @@ -1,61 +0,0 @@ -@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: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/composites/sequence_star.gd.uid b/addons/beehave/nodes/composites/sequence_star.gd.uid deleted file mode 100644 index a3b5533..0000000 --- a/addons/beehave/nodes/composites/sequence_star.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://22e1jnayd6d2 diff --git a/addons/beehave/nodes/composites/simple_parallel.gd b/addons/beehave/nodes/composites/simple_parallel.gd deleted file mode 100644 index 8511808..0000000 --- a/addons/beehave/nodes/composites/simple_parallel.gd +++ /dev/null @@ -1,122 +0,0 @@ -@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: int = c.get_index() - if node_index == 0 and not main_task_finished: - if c != running_child: - c.before_run(actor, blackboard) - - var response: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/composites/simple_parallel.gd.uid b/addons/beehave/nodes/composites/simple_parallel.gd.uid deleted file mode 100644 index bc1a116..0000000 --- a/addons/beehave/nodes/composites/simple_parallel.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bkd78br36f22s diff --git a/addons/beehave/nodes/decorators/cooldown.gd b/addons/beehave/nodes/decorators/cooldown.gd deleted file mode 100644 index bba1217..0000000 --- a/addons/beehave/nodes/decorators/cooldown.gd +++ /dev/null @@ -1,49 +0,0 @@ -@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: BeehaveNode = get_child(0) - var remaining_time: float = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id())) - var response: int - - 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, blackboard.get_debug_data()) - else: - response = c.tick(actor, blackboard) - - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/decorators/cooldown.gd.uid b/addons/beehave/nodes/decorators/cooldown.gd.uid deleted file mode 100644 index a2226e7..0000000 --- a/addons/beehave/nodes/decorators/cooldown.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://npam8q0cckw4 diff --git a/addons/beehave/nodes/decorators/decorator.gd b/addons/beehave/nodes/decorators/decorator.gd deleted file mode 100644 index 3b93498..0000000 --- a/addons/beehave/nodes/decorators/decorator.gd +++ /dev/null @@ -1,33 +0,0 @@ -@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 diff --git a/addons/beehave/nodes/decorators/decorator.gd.uid b/addons/beehave/nodes/decorators/decorator.gd.uid deleted file mode 100644 index 32fde99..0000000 --- a/addons/beehave/nodes/decorators/decorator.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ee7t7mdnh7wf diff --git a/addons/beehave/nodes/decorators/delayer.gd b/addons/beehave/nodes/decorators/delayer.gd deleted file mode 100644 index 6b023e4..0000000 --- a/addons/beehave/nodes/decorators/delayer.gd +++ /dev/null @@ -1,49 +0,0 @@ -@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: BeehaveNode = get_child(0) - var total_time: float = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id())) - var response: int - - 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, blackboard.get_debug_data()) - else: - response = c.tick(actor, blackboard) - - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/decorators/delayer.gd.uid b/addons/beehave/nodes/decorators/delayer.gd.uid deleted file mode 100644 index 02945a8..0000000 --- a/addons/beehave/nodes/decorators/delayer.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ch31ebso22wxa diff --git a/addons/beehave/nodes/decorators/failer.gd b/addons/beehave/nodes/decorators/failer.gd deleted file mode 100644 index e8c5396..0000000 --- a/addons/beehave/nodes/decorators/failer.gd +++ /dev/null @@ -1,35 +0,0 @@ -@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: BeehaveNode = get_child(0) - - if c != running_child: - c.before_run(actor, blackboard) - - var response: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/decorators/failer.gd.uid b/addons/beehave/nodes/decorators/failer.gd.uid deleted file mode 100644 index 6711487..0000000 --- a/addons/beehave/nodes/decorators/failer.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bm0vay28ba7dj diff --git a/addons/beehave/nodes/decorators/inverter.gd b/addons/beehave/nodes/decorators/inverter.gd deleted file mode 100644 index f6f38c5..0000000 --- a/addons/beehave/nodes/decorators/inverter.gd +++ /dev/null @@ -1,43 +0,0 @@ -@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: BeehaveNode = get_child(0) - - if c != running_child: - c.before_run(actor, blackboard) - - var response: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/decorators/inverter.gd.uid b/addons/beehave/nodes/decorators/inverter.gd.uid deleted file mode 100644 index acc1f0b..0000000 --- a/addons/beehave/nodes/decorators/inverter.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bvbkudic8q2p1 diff --git a/addons/beehave/nodes/decorators/limiter.gd b/addons/beehave/nodes/decorators/limiter.gd deleted file mode 100644 index 3b6127e..0000000 --- a/addons/beehave/nodes/decorators/limiter.gd +++ /dev/null @@ -1,60 +0,0 @@ -@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: int = 0 - - -func tick(actor: Node, blackboard: Blackboard) -> int: - if not get_child_count() == 1: - return FAILURE - - var child: BeehaveNode = get_child(0) - var current_count: int = 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: int = child.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response, blackboard.get_debug_data()) - - 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 [] diff --git a/addons/beehave/nodes/decorators/limiter.gd.uid b/addons/beehave/nodes/decorators/limiter.gd.uid deleted file mode 100644 index 9ede652..0000000 --- a/addons/beehave/nodes/decorators/limiter.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bsla11d2hsl4f diff --git a/addons/beehave/nodes/decorators/repeater.gd b/addons/beehave/nodes/decorators/repeater.gd deleted file mode 100644 index d106a38..0000000 --- a/addons/beehave/nodes/decorators/repeater.gd +++ /dev/null @@ -1,58 +0,0 @@ -## 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: BeehaveNode = get_child(0) - - if current_count < repetitions: - if running_child == null: - child.before_run(actor, blackboard) - - var response: int = child.tick(actor, blackboard) - - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/decorators/repeater.gd.uid b/addons/beehave/nodes/decorators/repeater.gd.uid deleted file mode 100644 index 37e4d25..0000000 --- a/addons/beehave/nodes/decorators/repeater.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b1tox1lfjp85u diff --git a/addons/beehave/nodes/decorators/succeeder.gd b/addons/beehave/nodes/decorators/succeeder.gd deleted file mode 100644 index 2a7d5ed..0000000 --- a/addons/beehave/nodes/decorators/succeeder.gd +++ /dev/null @@ -1,35 +0,0 @@ -@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: BeehaveNode = get_child(0) - - if c != running_child: - c.before_run(actor, blackboard) - - var response: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/decorators/succeeder.gd.uid b/addons/beehave/nodes/decorators/succeeder.gd.uid deleted file mode 100644 index d39ce6e..0000000 --- a/addons/beehave/nodes/decorators/succeeder.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dc4wovsitw6ds diff --git a/addons/beehave/nodes/decorators/time_limiter.gd b/addons/beehave/nodes/decorators/time_limiter.gd deleted file mode 100644 index c19c598..0000000 --- a/addons/beehave/nodes/decorators/time_limiter.gd +++ /dev/null @@ -1,60 +0,0 @@ -@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: String = "time_limiter_%s" % self.get_instance_id() - - -func tick(actor: Node, blackboard: Blackboard) -> int: - if not get_child_count() == 1: - return FAILURE - - var child: BeehaveNode = self.get_child(0) - var time_left: float = 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: int = child.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response, blackboard.get_debug_data()) - - 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 [] diff --git a/addons/beehave/nodes/decorators/time_limiter.gd.uid b/addons/beehave/nodes/decorators/time_limiter.gd.uid deleted file mode 100644 index 597018b..0000000 --- a/addons/beehave/nodes/decorators/time_limiter.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cx68vwulwsmbh diff --git a/addons/beehave/nodes/decorators/until_fail.gd b/addons/beehave/nodes/decorators/until_fail.gd deleted file mode 100644 index 82dc686..0000000 --- a/addons/beehave/nodes/decorators/until_fail.gd +++ /dev/null @@ -1,33 +0,0 @@ -@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: BeehaveNode = get_child(0) - - if c != running_child: - c.before_run(actor, blackboard) - - var response: int = c.tick(actor, blackboard) - if can_send_message(blackboard): - BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data()) - - 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 diff --git a/addons/beehave/nodes/decorators/until_fail.gd.uid b/addons/beehave/nodes/decorators/until_fail.gd.uid deleted file mode 100644 index 8fc5677..0000000 --- a/addons/beehave/nodes/decorators/until_fail.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c0agipyurkehb diff --git a/addons/beehave/nodes/leaves/action.gd b/addons/beehave/nodes/leaves/action.gd deleted file mode 100644 index 9074c07..0000000 --- a/addons/beehave/nodes/leaves/action.gd +++ /dev/null @@ -1,14 +0,0 @@ -@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 diff --git a/addons/beehave/nodes/leaves/action.gd.uid b/addons/beehave/nodes/leaves/action.gd.uid deleted file mode 100644 index bce09e8..0000000 --- a/addons/beehave/nodes/leaves/action.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b3lhrf7h086f2 diff --git a/addons/beehave/nodes/leaves/blackboard_compare.gd b/addons/beehave/nodes/leaves/blackboard_compare.gd deleted file mode 100644 index 38a870d..0000000 --- a/addons/beehave/nodes/leaves/blackboard_compare.gd +++ /dev/null @@ -1,65 +0,0 @@ -@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] diff --git a/addons/beehave/nodes/leaves/blackboard_compare.gd.uid b/addons/beehave/nodes/leaves/blackboard_compare.gd.uid deleted file mode 100644 index 7400462..0000000 --- a/addons/beehave/nodes/leaves/blackboard_compare.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://gnyeilxt2l0o diff --git a/addons/beehave/nodes/leaves/blackboard_erase.gd b/addons/beehave/nodes/leaves/blackboard_erase.gd deleted file mode 100644 index e5cc1d4..0000000 --- a/addons/beehave/nodes/leaves/blackboard_erase.gd +++ /dev/null @@ -1,25 +0,0 @@ -@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] diff --git a/addons/beehave/nodes/leaves/blackboard_erase.gd.uid b/addons/beehave/nodes/leaves/blackboard_erase.gd.uid deleted file mode 100644 index b7d22d7..0000000 --- a/addons/beehave/nodes/leaves/blackboard_erase.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://wj8886yb62i3 diff --git a/addons/beehave/nodes/leaves/blackboard_has.gd b/addons/beehave/nodes/leaves/blackboard_has.gd deleted file mode 100644 index 2ee5e92..0000000 --- a/addons/beehave/nodes/leaves/blackboard_has.gd +++ /dev/null @@ -1,23 +0,0 @@ -@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] diff --git a/addons/beehave/nodes/leaves/blackboard_has.gd.uid b/addons/beehave/nodes/leaves/blackboard_has.gd.uid deleted file mode 100644 index 623099c..0000000 --- a/addons/beehave/nodes/leaves/blackboard_has.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://blbjrtid0i0vk diff --git a/addons/beehave/nodes/leaves/blackboard_set.gd b/addons/beehave/nodes/leaves/blackboard_set.gd deleted file mode 100644 index 4f0ed9f..0000000 --- a/addons/beehave/nodes/leaves/blackboard_set.gd +++ /dev/null @@ -1,33 +0,0 @@ -@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] diff --git a/addons/beehave/nodes/leaves/blackboard_set.gd.uid b/addons/beehave/nodes/leaves/blackboard_set.gd.uid deleted file mode 100644 index fffe23b..0000000 --- a/addons/beehave/nodes/leaves/blackboard_set.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c82bmefi5j38r diff --git a/addons/beehave/nodes/leaves/condition.gd b/addons/beehave/nodes/leaves/condition.gd deleted file mode 100644 index f4610b4..0000000 --- a/addons/beehave/nodes/leaves/condition.gd +++ /dev/null @@ -1,12 +0,0 @@ -@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 diff --git a/addons/beehave/nodes/leaves/condition.gd.uid b/addons/beehave/nodes/leaves/condition.gd.uid deleted file mode 100644 index c7d77d9..0000000 --- a/addons/beehave/nodes/leaves/condition.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://2wnl5h3m16gh diff --git a/addons/beehave/nodes/leaves/leaf.gd b/addons/beehave/nodes/leaves/leaf.gd deleted file mode 100644 index 4946c7d..0000000 --- a/addons/beehave/nodes/leaves/leaf.gd +++ /dev/null @@ -1,48 +0,0 @@ -@tool -@icon("../../icons/category_leaf.svg") -class_name Leaf extends BeehaveNode - -## Base class for all leaf nodes of the tree. - -const EXPRESSION_PLACEHOLDER: String = "Insert an expression..." - - -func _get_configuration_warnings() -> PackedStringArray: - var warnings: PackedStringArray = [] - - var children: Array[Node] = get_children() - - if children.any(func(x): return x is BeehaveNode): - warnings.append("Leaf nodes should not have any child nodes. They won't be ticked.") - - for source in _get_expression_sources(): - var error_text: String = _parse_expression(source).get_error_text() - if not error_text.is_empty(): - warnings.append("Expression `%s` is invalid! Error text: `%s`" % [source, error_text]) - - return warnings - - -func _parse_expression(source: String) -> Expression: - var result: Expression = Expression.new() - var error: int = result.parse(source) - - if not Engine.is_editor_hint() and error != OK: - push_error( - ( - "[Leaf] Couldn't parse expression with source: `%s` Error text: `%s`" - % [source, result.get_error_text()] - ) - ) - - return result - - -func _get_expression_sources() -> Array[String]: # virtual - return [] - - -func get_class_name() -> Array[StringName]: - var classes := super() - classes.push_back(&"Leaf") - return classes diff --git a/addons/beehave/nodes/leaves/leaf.gd.uid b/addons/beehave/nodes/leaves/leaf.gd.uid deleted file mode 100644 index 7f72198..0000000 --- a/addons/beehave/nodes/leaves/leaf.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bybthnj000p24 diff --git a/addons/beehave/plugin.cfg b/addons/beehave/plugin.cfg deleted file mode 100644 index 5f1b671..0000000 --- a/addons/beehave/plugin.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[plugin] - -name="Beehave" -description="🐝 Behavior Tree addon for Godot Engine" -author="bitbrain" -version="2.8.4-dev" -script="plugin.gd" diff --git a/addons/beehave/plugin.gd b/addons/beehave/plugin.gd deleted file mode 100644 index cda3b0d..0000000 --- a/addons/beehave/plugin.gd +++ /dev/null @@ -1,26 +0,0 @@ -@tool -extends EditorPlugin - -const BeehaveEditorDebugger := preload("debug/debugger.gd") -var editor_debugger: BeehaveEditorDebugger -var frames: RefCounted - - -func _init(): - name = "BeehavePlugin" - add_autoload_singleton("BeehaveGlobalMetrics", "metrics/beehave_global_metrics.gd") - add_autoload_singleton("BeehaveGlobalDebugger", "debug/global_debugger.gd") - print("Beehave initialized!") - - -func _enter_tree() -> void: - editor_debugger = BeehaveEditorDebugger.new() - if Engine.get_version_info().minor >= 2: - frames = preload("debug/new_frames.gd").new() - else: - frames = preload("debug/old_frames.gd").new() - add_debugger_plugin(editor_debugger) - - -func _exit_tree() -> void: - remove_debugger_plugin(editor_debugger) diff --git a/addons/beehave/plugin.gd.uid b/addons/beehave/plugin.gd.uid deleted file mode 100644 index 4c943e8..0000000 --- a/addons/beehave/plugin.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://brq0oniu7r0o6 diff --git a/addons/beehave/utils/utils.gd b/addons/beehave/utils/utils.gd deleted file mode 100644 index 5f51ce7..0000000 --- a/addons/beehave/utils/utils.gd +++ /dev/null @@ -1,21 +0,0 @@ -@tool - - -static func get_plugin() -> EditorPlugin: - var tree: SceneTree = Engine.get_main_loop() - return tree.get_root().get_child(0).get_node_or_null("BeehavePlugin") - - -static func get_editor_scale() -> float: - var plugin := get_plugin() - if plugin: - return plugin.get_editor_interface().get_editor_scale() - return 1.0 - - -static func get_frames() -> RefCounted: - var plugin := get_plugin() - if plugin: - return plugin.frames - push_error("Can't find Beehave Plugin") - return null diff --git a/addons/beehave/utils/utils.gd.uid b/addons/beehave/utils/utils.gd.uid deleted file mode 100644 index 06af80a..0000000 --- a/addons/beehave/utils/utils.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://0pxtgaf20nnl diff --git a/project.godot b/project.godot index 0c60023..417c9ba 100644 --- a/project.godot +++ b/project.godot @@ -17,8 +17,6 @@ config/icon="res://icon.svg" [autoload] -BeehaveGlobalMetrics="*res://addons/beehave/metrics/beehave_global_metrics.gd" -BeehaveGlobalDebugger="*res://addons/beehave/debug/global_debugger.gd" NakamaManager="*res://scripts/nakama_manager.gd" NetworkManager="*res://scripts/network_manager.gd" Nakama="*res://addons/com.heroiclabs.nakama/Nakama.gd" diff --git a/scenes/lobby.gd b/scenes/lobby.gd index df2fae6..19e3afa 100644 --- a/scenes/lobby.gd +++ b/scenes/lobby.gd @@ -452,7 +452,37 @@ func _update_player_slots() -> void: ready_label.add_theme_color_override("font_color", Color(0.4, 0.8, 0.4) if is_ready else Color(0.6, 0.6, 0.6)) else: - slot.visible = false + # Empty slot - show as bot placeholder + slot.visible = true + + # Update name to show as bot + var name_label = slot.get_node_or_null("PlayerName%d" % slot_num) + if name_label: + name_label.text = "🤖 Bot %d" % slot_num + + # Use a character for bot preview + var char_preview = slot.get_node_or_null("CharacterPreview%d" % slot_num) + var bot_characters = ["Bob", "Gatot", "Masbro", "Oldpop"] + var bot_char = bot_characters[(i) % bot_characters.size()] + if char_preview and character_textures.has(bot_char): + char_preview.texture = character_textures[bot_char] + + # Hide character navigation for bots + var char_nav = slot.get_node_or_null("CharacterNav%d" % slot_num) + if char_nav: + char_nav.visible = false + + # Update character name label + var char_name_label = slot.get_node_or_null("CharacterNameLabel%d" % slot_num) + if char_name_label: + char_name_label.text = bot_char + char_name_label.visible = true + + # Show ready status as waiting + var ready_label = slot.get_node_or_null("ReadyStatus%d" % slot_num) + if ready_label: + ready_label.text = "WAITING..." + ready_label.add_theme_color_override("font_color", Color(0.5, 0.5, 0.7)) func _update_status() -> void: var players = LobbyManager.get_players() diff --git a/scenes/main.gd b/scenes/main.gd index c170527..db5bd4e 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -342,9 +342,10 @@ func _setup_host_game(): call_deferred("_deferred_set_player_goals", peer_id, client_goals) player_index += 1 - # Add bots (only if no lobby players connected) - if GameStateManager.enable_bots and lobby_players.size() <= 1: - for i in range(2, GameStateManager.max_players + 1): + # Add bots to fill remaining slots (regardless of player count) + if GameStateManager.enable_bots: + var current_players = lobby_players.size() + for i in range(current_players + 1, GameStateManager.max_players + 1): _add_bot(i) _start_game() @@ -777,7 +778,8 @@ func update_all_players_boards(): func verify_all_connections(): if multiplayer.is_server(): for peer_id in GameStateManager.players: - if peer_id != 1: + # Skip host (1) and bots (bots don't have real network connections) + if peer_id != 1 and not peer_id in GameStateManager.bots: rpc_id(peer_id, "connection_verify", GameStateManager.players) @rpc @@ -956,9 +958,10 @@ func _on_leaderboard_updated(sorted_scores: Array): var player_data = [] for p in get_tree().get_nodes_in_group("Players"): player_data.append({ - "peer_id": p.get_multiplayer_authority(), + # Use name.to_int() to correctly identify bots (Authority 1) vs Players + "peer_id": p.name.to_int(), "name": p.display_name if not p.display_name.is_empty() else str(p.name), - "score": goals_cycle_manager.get_player_score(p.get_multiplayer_authority()) if goals_cycle_manager else 0 + "score": goals_cycle_manager.get_player_score(p.name.to_int()) if goals_cycle_manager else 0 }) rpc("sync_leaderboard_data", player_data) @@ -1148,9 +1151,10 @@ func request_leaderboard_sync(): var player_data = [] for p in get_tree().get_nodes_in_group("Players"): player_data.append({ - "peer_id": p.get_multiplayer_authority(), + # Use name.to_int() for consistent ID + "peer_id": p.name.to_int(), "name": p.display_name if not p.display_name.is_empty() else str(p.name), - "score": goals_cycle_manager.get_player_score(p.get_multiplayer_authority()) if goals_cycle_manager else 0 + "score": goals_cycle_manager.get_player_score(p.name.to_int()) if goals_cycle_manager else 0 }) rpc_id(sender_id, "sync_leaderboard_data", player_data) @@ -1209,7 +1213,7 @@ func _update_leaderboard_display(): # Build scores array with all players var player_data = [] for p in all_players: - var peer_id = p.get_multiplayer_authority() + var peer_id = p.name.to_int() var score = goals_cycle_manager.get_player_score(peer_id) if goals_cycle_manager else 0 player_data.append({"peer_id": peer_id, "name": p.display_name if not p.display_name.is_empty() else str(p.name), "score": score}) diff --git a/scenes/player.gd b/scenes/player.gd index 648a1d0..6de7c56 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -119,19 +119,28 @@ const AVAILABLE_CHARACTERS: Array[String] = ["Bob", "Masbro", "Gatot", "Oldpop"] func _ready(): # Ensure name is set first (node name = authority ID for multiplayer identification) - name = str(get_multiplayer_authority()) - + # name = str(get_multiplayer_authority()) # CRITICAL FIX: Do NOT overwrite name. Bots have authority 1 but unique names (IDs). # Look up player's display name from LobbyManager var my_id = get_multiplayer_authority() - for player_data in LobbyManager.get_players(): - if player_data.get("id") == my_id: - display_name = player_data.get("name", "Player") - break + + if is_bot or is_in_group("Bots"): + # Bots get a unique name based on their Node Name (Bot ID) + var bot_id = name.to_int() + var bot_names = ["Bot", "Alpha", "Beta", "Gamma", "Delta"] + var name_idx = (bot_id - 1) % bot_names.size() + display_name = "%s %d" % ["Bot", bot_id] + else: + # Humans get name from Lobby + for player_data in LobbyManager.get_players(): + if player_data.get("id") == my_id: + display_name = player_data.get("name", "Player") + break + if display_name.is_empty(): display_name = "Player %d" % my_id $Name.text = display_name - # Sync name to other peers if this is our local player + # Sync name to other peers if this is our local player or a bot we own if is_multiplayer_authority(): rpc("sync_display_name", display_name) @@ -154,77 +163,58 @@ func _ready(): # Initialize character selection from LobbyManager _setup_character() - # Early setup for bots + # Initialize playerboard for everyone + playerboard.resize(25) + playerboard.fill(-1) + + # ========================================================================= + # BOT-SPECIFIC SETUP - BotController handles bot AI, we just disable input + # ========================================================================= if is_bot == true or is_in_group("Bots"): - # Initialize behavior tree for bots - var behavior_tree = $BehaviorTree - # Disable all input processing for bots immediately + # Disable input processing for bots set_process_input(false) set_process_unhandled_input(false) - set_process(false) - set_physics_process(false) - # Disable visual highlights for bots - action_manager.highlighted_cells.clear() - - if behavior_tree: - behavior_tree.enabled = is_multiplayer_authority() - behavior_tree.actor = self - - rpc("sync_bot_status", true) - - ## Initialize bot-specific components - #if enhanced_gridmap: - #current_position = find_valid_starting_position() - #update_player_position(current_position) - - # Spawn point handler + # Set initial position for bots if enhanced_gridmap: current_position = _find_random_spawn_position() update_player_position(current_position) spawn_point_selected = true - # Notify others about bot spawn position - rpc("notify_spawn_selected", current_position) + if is_multiplayer_authority(): + rpc("notify_spawn_selected", current_position) - else: - # Human player initialization - if enhanced_gridmap: - enhanced_gridmap.initialize_astar() - enhanced_gridmap.set_diagonal_movement(use_diagonal_movement) - - # Skip manual spawn selection if random spawn is enabled - # Host will assign positions via RPC - if not LobbyManager.get_randomize_spawn(): - # Request current spawn positions before highlighting - request_spawn_positions_update() - highlight_available_spawn_points() - # Remove this line as goals are now managed by the host - #append_random_goals() - - playerboard.resize(25) - playerboard.fill(-1) - return + # Assign bot character (deterministic based on ID to match lobby preview) + # Bot IDs start from 2 (host is 1) + # Lobby slots are 0-indexed in UI loop, but bots fill empty slots. + # Use name.to_int() because all bots have authority 1 (Server) + var bot_id_val = name.to_int() + var bot_characters = ["Bob", "Gatot", "Masbro", "Oldpop"] + # Map bot ID to character index. Bot 2 -> Index 1. Bot 3 -> Index 2. + # Formula: (bot_id - 1) % size + var char_index = (bot_id_val - 1) % bot_characters.size() + var bot_char_name = bot_characters[char_index] + set_character(bot_char_name) + if is_multiplayer_authority(): + rpc("sync_character", bot_char_name) + + # Sync bot status to network + if is_multiplayer_authority(): + rpc("sync_bot_status", true) + return # Bot initialization complete - BotController handles AI - # Disable Beehave tree if this is not a bot - if not is_bot and has_node("BehaviorTree"): - $BehaviorTree.enabled = false - - # Rest of initialization (only for human players) + # ========================================================================= + # HUMAN PLAYER SETUP + # ========================================================================= if enhanced_gridmap: enhanced_gridmap.initialize_astar() enhanced_gridmap.set_diagonal_movement(use_diagonal_movement) + # Only set position if not using random spawn (host will assign via RPC) - # AND not already assigned if not LobbyManager.get_randomize_spawn() and not spawn_point_selected: current_position = find_valid_starting_position() update_player_position(current_position) - #append_random_goals() - playerboard.resize(25) - playerboard.fill(-1) - - # Ensure proper initial positioning (only if NOT using random spawn and not already positioned) - # When random spawn is enabled, the host assigns positions via set_spawn_position RPC + # Ensure proper initial positioning if not LobbyManager.get_randomize_spawn() and not spawn_point_selected: global_position = Vector3( current_position.x * cell_size.x + cell_size.x * 0.5, @@ -321,6 +311,11 @@ func sync_character(character_name: String) -> void: func _setup_character() -> void: """Initialize character based on LobbyManager selection or defaults.""" + # Bots self-assign characters based on ID in _ready() + # Skipping this for bots prevents race conditions or defaults overriding deterministic bot logic + if is_bot or is_in_group("Bots"): + return + var character_name = "Masbro" # Default var player_authority_id = get_multiplayer_authority() @@ -505,6 +500,29 @@ func receive_spawn_positions_update(occupied_positions: Array): # Now highlight available positions highlight_available_spawn_points() +@rpc("any_peer", "call_local", "reliable") +func sync_playerboard(new_playerboard: Array): + """Sync playerboard data. Called by BotController or ActionManager.""" + playerboard = new_playerboard.duplicate() + + # Only update UI if this is the LOCAL HUMAN PLAYER + # Bots managed by server (ID 1) might sync here, but we must NOT update UI for them + var is_local = name == str(multiplayer.get_unique_id()) + var is_bot_check = is_bot or is_in_group("Bots") + + # DEBUG: Trace why UI updates might be happening + # print("[sync_playerboard] Player: %s (ID: %s), LocalID: %s, IsLocal: %s, IsBot: %s" % [name, get_multiplayer_authority(), multiplayer.get_unique_id(), is_local, is_bot_check]) + + if is_local and not is_bot_check: + var main = get_tree().get_root().get_node_or_null("Main") + if main and main.ui_manager: + main.ui_manager.update_playerboard_ui() + elif is_local and is_bot_check: + # This is a Bot running on the server (which is also the host) + # We must NOT update the Host's UI with the Bot's data + pass + + @rpc("any_peer", "call_local") func sync_bot_status(is_bot_status: bool): is_bot = is_bot_status @@ -513,18 +531,11 @@ func sync_bot_status(is_bot_status: bool): set_process_input(false) set_process_unhandled_input(false) - # Clear any existing highlights - action_manager.highlighted_cells.clear() - #clear_highlights() - #clear_playerboard_highlights() + # Clear any existing highlights (if action_manager is initialized) + if action_manager: + action_manager.highlighted_cells.clear() - var behavior_tree = get_node_or_null("BehaviorTree") - if behavior_tree: - behavior_tree.enabled = is_multiplayer_authority() - behavior_tree.actor = self - if not is_multiplayer_authority(): - behavior_tree.set_physics_process(false) - behavior_tree.set_process(false) + # BotController already exists in scene and manages itself @rpc("any_peer", "call_local", "reliable") func sync_display_name(new_name: String) -> void: @@ -1259,13 +1270,6 @@ func sync_second_lap_goals(new_goals: Array): if race_manager: race_manager.second_lap_goals = new_goals.duplicate() -@rpc("any_peer", "call_local") -func sync_playerboard(new_playerboard: Array): - playerboard = new_playerboard.duplicate() - var main = get_tree().get_root().get_node_or_null("Main") - if main: - main.rpc("sync_playerboard", get_multiplayer_authority(), playerboard) - action_manager.after_action_completed() func _after_action_completed(): action_manager.after_action_completed() diff --git a/scenes/player.tscn b/scenes/player.tscn index 4912af3..f4cc876 100644 --- a/scenes/player.tscn +++ b/scenes/player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=25 format=3 uid="uid://1dbdbg3q5778"] +[gd_scene load_steps=13 format=3 uid="uid://1dbdbg3q5778"] [ext_resource type="Script" uid="uid://c78jcadupsdro" path="res://scenes/player.gd" id="1_qecr4"] [ext_resource type="PackedScene" uid="uid://ejeamn0pyey4" path="res://assets/characters/Bob.glb" id="2_3e0d5"] @@ -7,19 +7,7 @@ [ext_resource type="PackedScene" uid="uid://d4cul3w3wem5w" path="res://assets/characters/Gatot.glb" id="4_3tlf6"] [ext_resource type="PackedScene" uid="uid://bmln7v6v5kvxg" path="res://assets/characters/Oldpop.glb" id="5_alfd1"] [ext_resource type="AnimationLibrary" uid="uid://c3pyopnwibckj" path="res://assets/characters/animations/animation-pack.res" id="6_5oq5w"] -[ext_resource type="Script" uid="uid://6g75rh3nj2s6" path="res://scripts/bot_behavior.gd" id="8_1o2fn"] -[ext_resource type="Script" uid="uid://dtctf507t71tj" path="res://addons/beehave/nodes/composites/selector.gd" id="9_jspru"] -[ext_resource type="Script" uid="uid://t6e8rtdiqhg5" path="res://addons/beehave/nodes/composites/sequence.gd" id="10_hv4ee"] -[ext_resource type="Script" uid="uid://b17qem72laaeb" path="res://scripts/behaviors/conditions/has_ap.gd" id="11_7fhpq"] -[ext_resource type="Script" uid="uid://b4fxorcb1yq17" path="res://scripts/behaviors/actions/do_arrange.gd" id="12_1ppih"] -[ext_resource type="Script" uid="uid://b25qg75d0xgkh" path="res://scripts/behaviors/conditions/can_arrange.gd" id="12_hr248"] -[ext_resource type="Script" uid="uid://cui40g7qjf1y3" path="res://scripts/behaviors/conditions/can_grab.gd" id="13_41jsv"] -[ext_resource type="Script" uid="uid://cuyorbwefmh0y" path="res://scripts/behaviors/actions/do_grab.gd" id="15_5h472"] -[ext_resource type="Script" uid="uid://b7y30e5mxygj0" path="res://scripts/behaviors/conditions/can_put.gd" id="16_ac2sy"] -[ext_resource type="Script" uid="uid://bdw5bwmr32h63" path="res://scripts/behaviors/actions/do_put.gd" id="17_e03nk"] -[ext_resource type="Script" uid="uid://bc7jpc1bwy4dg" path="res://scripts/behaviors/conditions/should_move.gd" id="18_2ghcp"] -[ext_resource type="Script" uid="uid://cireifbxafgf2" path="res://scripts/behaviors/actions/do_move.gd" id="19_dl4fn"] -[ext_resource type="Script" uid="uid://d2cr28ak2s1rr" path="res://scripts/bot_blackboard.gd" id="20_24ja6"] +[ext_resource type="Script" uid="uid://cwwwixc07jc86" path="res://scripts/bot_controller.gd" id="7_botctrl"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_xqgey"] albedo_color = Color(0.85, 0.085, 0.238, 1) @@ -100,61 +88,5 @@ autowrap_mode = 3 justification_flags = 171 width = 700.0 -[node name="Blackboard" type="Node" parent="."] -script = ExtResource("20_24ja6") - -[node name="BehaviorTree" type="Node" parent="." node_paths=PackedStringArray("blackboard")] -script = ExtResource("8_1o2fn") -tick_rate = 90 -blackboard = NodePath("../Blackboard") - -[node name="Selector" type="Node" parent="BehaviorTree"] -script = ExtResource("9_jspru") - -[node name="GrabSequence" type="Node" parent="BehaviorTree/Selector"] -script = ExtResource("10_hv4ee") - -[node name="HasAP" type="Node" parent="BehaviorTree/Selector/GrabSequence"] -script = ExtResource("11_7fhpq") - -[node name="CanGrab" type="Node" parent="BehaviorTree/Selector/GrabSequence"] -script = ExtResource("13_41jsv") - -[node name="DoGrab" type="Node" parent="BehaviorTree/Selector/GrabSequence"] -script = ExtResource("15_5h472") - -[node name="MoveSequence" type="Node" parent="BehaviorTree/Selector"] -script = ExtResource("10_hv4ee") - -[node name="HasAP" type="Node" parent="BehaviorTree/Selector/MoveSequence"] -script = ExtResource("11_7fhpq") - -[node name="ShouldMove" type="Node" parent="BehaviorTree/Selector/MoveSequence"] -script = ExtResource("18_2ghcp") - -[node name="DoMove" type="Node" parent="BehaviorTree/Selector/MoveSequence"] -script = ExtResource("19_dl4fn") - -[node name="PutSequence" type="Node" parent="BehaviorTree/Selector"] -script = ExtResource("10_hv4ee") - -[node name="HasAP" type="Node" parent="BehaviorTree/Selector/PutSequence"] -script = ExtResource("11_7fhpq") - -[node name="CanPut" type="Node" parent="BehaviorTree/Selector/PutSequence"] -script = ExtResource("16_ac2sy") - -[node name="DoPut" type="Node" parent="BehaviorTree/Selector/PutSequence"] -script = ExtResource("17_e03nk") - -[node name="ArrangeSequence" type="Node" parent="BehaviorTree/Selector"] -script = ExtResource("10_hv4ee") - -[node name="HasEnoughAP" type="Node" parent="BehaviorTree/Selector/ArrangeSequence"] -script = ExtResource("11_7fhpq") - -[node name="CanArrange" type="Node" parent="BehaviorTree/Selector/ArrangeSequence"] -script = ExtResource("12_hr248") - -[node name="DoArrange" type="Node" parent="BehaviorTree/Selector/ArrangeSequence"] -script = ExtResource("12_1ppih") +[node name="BotController" type="Node" parent="."] +script = ExtResource("7_botctrl") diff --git a/scripts/behaviors/actions/do_arrange.gd b/scripts/behaviors/actions/do_arrange.gd deleted file mode 100644 index d28749b..0000000 --- a/scripts/behaviors/actions/do_arrange.gd +++ /dev/null @@ -1,87 +0,0 @@ -extends ActionLeaf - -func find_best_arrangement(actor) -> Dictionary: - # Convert goals to 2D array for easier comparison - var goals_2d = [] - for i in range(3): - var row = [] - for j in range(3): - row.append(actor.goals[i * 3 + j]) - goals_2d.append(row) - - # Convert playerboard to 2D - var board_2d = [] - for i in range(5): - var row = [] - for j in range(5): - row.append(actor.playerboard[i * 5 + j]) - board_2d.append(row) - - # Find misplaced items - for i in range(1, 4): # Check central 3x3 area - for j in range(1, 4): - var board_idx = i * 5 + j - var goal_idx = (i - 1) * 3 + (j - 1) - - var current_item = actor.playerboard[board_idx] - var goal_item = actor.goals[goal_idx] - - if current_item != goal_item and current_item != -1: - # Find better position for this item - for gi in range(3): - for gj in range(3): - if actor.goals[gi * 3 + gj] == current_item: - var target_slot = (gi + 1) * 5 + (gj + 1) - if actor.playerboard[target_slot] == -1: - return { - "source_slot": board_idx, - "target_slot": target_slot - } - - return {} - -func is_goals_achieved(actor) -> bool: - # Check only central 3x3 area of playerboard against goals - for i in range(3): - for j in range(3): - var board_idx = (i + 1) * 5 + (j + 1) - var goal_idx = i * 3 + j - - if actor.goals[goal_idx] != -1 and actor.goals[goal_idx] != actor.playerboard[board_idx]: - return false - elif actor.goals[goal_idx] == -1 and actor.playerboard[board_idx] != -1: - return false - - # Also check outside the goal area - if i == 0 or i == 4 or j == 0 or j == 4: - if actor.playerboard[i * 5 + j] != -1: - return false - - return true - -func tick(actor: Node, blackboard: Blackboard) -> int: - # First check if goals are already achieved - if is_goals_achieved(actor): - blackboard.set_value("current_action", "idle") - return FAILURE - - if actor.action_points < 2: - return FAILURE - - var arrangement = find_best_arrangement(actor) - if arrangement.is_empty(): - return FAILURE - - blackboard.set_value("source_slot", arrangement.source_slot) - blackboard.set_value("target_slot", arrangement.target_slot) - - # Do the arrangement - if actor.is_multiplayer_authority(): - var item = actor.playerboard[arrangement.source_slot] - actor.playerboard[arrangement.target_slot] = item - actor.playerboard[arrangement.source_slot] = -1 - actor.rpc("sync_playerboard", actor.playerboard) - actor.has_performed_action = true - actor.action_points -= 2 - - return SUCCESS diff --git a/scripts/behaviors/actions/do_arrange.gd.uid b/scripts/behaviors/actions/do_arrange.gd.uid deleted file mode 100644 index ecb5d94..0000000 --- a/scripts/behaviors/actions/do_arrange.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b4fxorcb1yq17 diff --git a/scripts/behaviors/actions/do_grab.gd b/scripts/behaviors/actions/do_grab.gd deleted file mode 100644 index 7d871cb..0000000 --- a/scripts/behaviors/actions/do_grab.gd +++ /dev/null @@ -1,110 +0,0 @@ -extends ActionLeaf - -func is_goals_achieved(actor) -> bool: - # Convert goals to 2D for easier pattern matching - var goals_2d = [] - for i in range(3): - var row = [] - for j in range(3): - row.append(actor.goals[i * 3 + j]) - goals_2d.append(row) - - # Convert playerboard to 2D - var board_2d = [] - for i in range(5): - var row = [] - for j in range(5): - row.append(actor.playerboard[i * 5 + j]) - board_2d.append(row) - - # Check every possible 3x3 region in the 5x5 board - for start_row in range(3): # 5-3+1 possible start rows - for start_col in range(3): # 5-3+1 possible start columns - var matches = true - - # Check if this 3x3 region matches the goals - for i in range(3): - for j in range(3): - var board_item = board_2d[start_row + i][start_col + j] - var goal_item = goals_2d[i][j] - - if goal_item != -1 and goal_item != board_item: - matches = false - break - elif goal_item == -1 and board_item != -1: - matches = false - break - - if not matches: - break - - if matches: - return true - - return false - -func find_target_slot(actor, item: int) -> int: - # Get the goals in 2D format (3x3) - var goals_2d = [] - for i in range(3): - var row = [] - for j in range(3): - row.append(actor.goals[i * 3 + j]) - goals_2d.append(row) - - # Convert playerboard to 2D (5x5) - var board_2d = [] - for i in range(5): - var row = [] - for j in range(5): - row.append(actor.playerboard[i * 5 + j]) - board_2d.append(row) - - # Find matching position in goals - var target_positions = [] - for i in range(3): - for j in range(3): - if goals_2d[i][j] == item: - # Convert to playerboard position (centered in 5x5 grid) - target_positions.append(Vector2i(j + 1, i + 1)) - - # Find best empty slot that matches a goal position - for pos in target_positions: - var slot_index = (pos.y + 1) * 5 + (pos.x + 1) - if actor.playerboard[slot_index] == -1: - return slot_index - - # If no matching position found, find any empty slot - return actor.playerboard.find(-1) - -func tick(actor: Node, blackboard: Blackboard) -> int: - # Early validation - if not actor.is_bot or actor.action_points <= 0: - return FAILURE - - var grab_position = blackboard.get_value("grab_position") - if not grab_position: - return FAILURE - - # Check if there's an item - var cell = Vector3i(grab_position.x, 1, grab_position.y) - var item = actor.enhanced_gridmap.get_cell_item(cell) - if item == -1: - return FAILURE - - # Find strategic slot - var target_slot = find_target_slot(actor, item) - if target_slot == -1: - return FAILURE - - # Execute grab - if actor.is_multiplayer_authority(): - actor.playerboard[target_slot] = item - actor.rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) - actor.rpc("sync_playerboard", actor.playerboard) - actor.has_performed_action = true - actor.action_points -= 1 - blackboard.set_value("current_action", "idle") - actor._after_action_completed() # Add this line - - return SUCCESS diff --git a/scripts/behaviors/actions/do_grab.gd.uid b/scripts/behaviors/actions/do_grab.gd.uid deleted file mode 100644 index a0c1d71..0000000 --- a/scripts/behaviors/actions/do_grab.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cuyorbwefmh0y diff --git a/scripts/behaviors/actions/do_move.gd b/scripts/behaviors/actions/do_move.gd deleted file mode 100644 index 7611871..0000000 --- a/scripts/behaviors/actions/do_move.gd +++ /dev/null @@ -1,46 +0,0 @@ -extends ActionLeaf - -func is_goals_achieved(actor) -> bool: - for i in range(3): - for j in range(3): - var board_idx = (i + 1) * 5 + (j + 1) - var goal_idx = i * 3 + j - if actor.goals[goal_idx] != -1 and actor.goals[goal_idx] != actor.playerboard[board_idx]: - return false - elif actor.goals[goal_idx] == -1 and actor.playerboard[board_idx] != -1: - return false - return true - -func tick(actor: Node, blackboard: Blackboard) -> int: - # Don't move if goals are achieved - if is_goals_achieved(actor): - return FAILURE - - # Get target from blackboard - var target_pos = blackboard.get_value("move_target") - if not target_pos: - return FAILURE - - if actor.action_points <= 0: - return FAILURE - - if not actor.is_bot == true and not actor.is_in_group("Bots"): - return FAILURE - - # Execute movement - if actor.is_within_movement_range(target_pos): - if actor.is_bot == true: - var path = actor.enhanced_gridmap.find_path( - Vector2(actor.current_position), - Vector2(target_pos), - 0, - false - ) - if path.size() > 1: - path.pop_front() - actor.rpc("start_movement_along_path", path, false) - actor.action_points -= 1 - blackboard.set_value("current_action", "moving") - return SUCCESS - - return FAILURE diff --git a/scripts/behaviors/actions/do_move.gd.uid b/scripts/behaviors/actions/do_move.gd.uid deleted file mode 100644 index e4482f5..0000000 --- a/scripts/behaviors/actions/do_move.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cireifbxafgf2 diff --git a/scripts/behaviors/actions/do_put.gd b/scripts/behaviors/actions/do_put.gd deleted file mode 100644 index 1f8a8c0..0000000 --- a/scripts/behaviors/actions/do_put.gd +++ /dev/null @@ -1,47 +0,0 @@ -extends ActionLeaf - -func is_goals_achieved(actor) -> bool: - # Check only central 3x3 area of playerboard against goals - for i in range(3): - for j in range(3): - var board_idx = (i + 1) * 5 + (j + 1) - var goal_idx = i * 3 + j - - if actor.goals[goal_idx] != -1 and actor.goals[goal_idx] != actor.playerboard[board_idx]: - return false - elif actor.goals[goal_idx] == -1 and actor.playerboard[board_idx] != -1: - return false - - # Also check outside the goal area - if i == 0 or i == 4 or j == 0 or j == 4: - if actor.playerboard[i * 5 + j] != -1: - return false - - return true - -func tick(actor: Node, blackboard: Blackboard) -> int: - var put_position = blackboard.get_value("put_position") - var put_slot = blackboard.get_value("put_slot") - - if not put_position or put_slot == -1: - return FAILURE - - # Check if we still have the item and AP - if actor.action_points <= 0 or actor.playerboard[put_slot] == -1: - return FAILURE - - # Check if target position is still empty - var cell = Vector3i(put_position.x, 1, put_position.y) - if actor.enhanced_gridmap.get_cell_item(cell) != -1: - return FAILURE - - # Execute put - var item = actor.playerboard[put_slot] - if actor.is_multiplayer_authority(): - actor.rpc("sync_grid_item", cell.x, cell.y, cell.z, item) - actor.playerboard[put_slot] = -1 - actor.rpc("sync_playerboard", actor.playerboard) - actor.has_performed_action = true - actor.action_points -= 1 - - return SUCCESS diff --git a/scripts/behaviors/actions/do_put.gd.uid b/scripts/behaviors/actions/do_put.gd.uid deleted file mode 100644 index e65af6f..0000000 --- a/scripts/behaviors/actions/do_put.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bdw5bwmr32h63 diff --git a/scripts/behaviors/conditions/can_arrange.gd b/scripts/behaviors/conditions/can_arrange.gd deleted file mode 100644 index abc3236..0000000 --- a/scripts/behaviors/conditions/can_arrange.gd +++ /dev/null @@ -1,16 +0,0 @@ -extends ConditionLeaf - -func tick(actor: Node, blackboard: Blackboard) -> int: - if actor.action_points < 2: - return FAILURE - - # Look for items that match goals - for i in range(actor.playerboard.size()): - if actor.playerboard[i] in actor.goals: - var neighbors = actor.get_adjacent_playerboard_slots(i) - for adj_slot in neighbors: - if actor.playerboard[adj_slot] == -1: - blackboard.set_value("source_slot", i) - blackboard.set_value("target_slot", adj_slot) - return SUCCESS - return FAILURE diff --git a/scripts/behaviors/conditions/can_arrange.gd.uid b/scripts/behaviors/conditions/can_arrange.gd.uid deleted file mode 100644 index 3f344ed..0000000 --- a/scripts/behaviors/conditions/can_arrange.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b25qg75d0xgkh diff --git a/scripts/behaviors/conditions/can_grab.gd b/scripts/behaviors/conditions/can_grab.gd deleted file mode 100644 index 905d9af..0000000 --- a/scripts/behaviors/conditions/can_grab.gd +++ /dev/null @@ -1,68 +0,0 @@ -extends ConditionLeaf - -func is_goals_achieved(actor) -> bool: - # Convert goals to 2D for easier pattern matching - var goals_2d = [] - for i in range(3): - var row = [] - for j in range(3): - row.append(actor.goals[i * 3 + j]) - goals_2d.append(row) - - # Convert playerboard to 2D - var board_2d = [] - for i in range(5): - var row = [] - for j in range(5): - row.append(actor.playerboard[i * 5 + j]) - board_2d.append(row) - - # Check every possible 3x3 region in the 5x5 board - for start_row in range(3): - for start_col in range(3): - var matches = true - for i in range(3): - for j in range(3): - var board_item = board_2d[start_row + i][start_col + j] - var goal_item = goals_2d[i][j] - - if goal_item != -1 and goal_item != board_item: - matches = false - break - elif goal_item == -1 and board_item != -1: - matches = false - break - if not matches: - break - if matches: - return true - return false - -func tick(actor: Node, blackboard: Blackboard) -> int: - # First check if goals are achieved - if is_goals_achieved(actor): - blackboard.set_value("current_action", "idle") - return FAILURE - - if actor.playerboard_is_full(): - return FAILURE - - # Check current position - var current_cell = Vector3i(actor.current_position.x, 1, actor.current_position.y) - var item = actor.enhanced_gridmap.get_cell_item(current_cell) - if item in actor.goals: - blackboard.set_value("grab_position", actor.current_position) - return SUCCESS - - # Check adjacent cells - var neighbors = actor.enhanced_gridmap.get_neighbors(actor.current_position, 0) - for neighbor in neighbors: - if not neighbor.is_walkable: - continue - var cell = Vector3i(neighbor.position.x, 1, neighbor.position.y) - item = actor.enhanced_gridmap.get_cell_item(cell) - if item in actor.goals: - blackboard.set_value("grab_position", neighbor.position) - return SUCCESS - - return FAILURE diff --git a/scripts/behaviors/conditions/can_grab.gd.uid b/scripts/behaviors/conditions/can_grab.gd.uid deleted file mode 100644 index 1e12c6b..0000000 --- a/scripts/behaviors/conditions/can_grab.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cui40g7qjf1y3 diff --git a/scripts/behaviors/conditions/can_put.gd b/scripts/behaviors/conditions/can_put.gd deleted file mode 100644 index 70b8a55..0000000 --- a/scripts/behaviors/conditions/can_put.gd +++ /dev/null @@ -1,40 +0,0 @@ -extends ConditionLeaf - -func is_goals_achieved(actor) -> bool: - # ... same is_goals_achieved function as in can_grab.gd ... - return false - -func tick(actor: Node, blackboard: Blackboard) -> int: - # First check if goals are achieved - if is_goals_achieved(actor): - blackboard.set_value("current_action", "idle") - return FAILURE - - # Find an item in playerboard that matches goals - var put_slot = -1 - for i in range(actor.playerboard.size()): - if actor.playerboard[i] in actor.goals: - put_slot = i - break - - if put_slot == -1: - return FAILURE - - # Find empty adjacent cell - var current_cell = Vector3i(actor.current_position.x, 1, actor.current_position.y) - if actor.enhanced_gridmap.get_cell_item(current_cell) == -1: - blackboard.set_value("put_position", actor.current_position) - blackboard.set_value("put_slot", put_slot) - return SUCCESS - - var neighbors = actor.enhanced_gridmap.get_neighbors(actor.current_position, 0) - for neighbor in neighbors: - if not neighbor.is_walkable: - continue - var cell = Vector3i(neighbor.position.x, 1, neighbor.position.y) - if actor.enhanced_gridmap.get_cell_item(cell) == -1: - blackboard.set_value("put_position", neighbor.position) - blackboard.set_value("put_slot", put_slot) - return SUCCESS - - return FAILURE diff --git a/scripts/behaviors/conditions/can_put.gd.uid b/scripts/behaviors/conditions/can_put.gd.uid deleted file mode 100644 index 7258c3e..0000000 --- a/scripts/behaviors/conditions/can_put.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b7y30e5mxygj0 diff --git a/scripts/behaviors/conditions/has_ap.gd b/scripts/behaviors/conditions/has_ap.gd deleted file mode 100644 index fc92a5e..0000000 --- a/scripts/behaviors/conditions/has_ap.gd +++ /dev/null @@ -1,18 +0,0 @@ -extends ConditionLeaf - -func tick(actor: Node, blackboard: Blackboard) -> int: - var main = get_tree().get_root().get_node_or_null("Main") - if not main: - return FAILURE - - # Always return SUCCESS for bots in non-turn-based mode - if actor.is_bot and not TurnManager.turn_based_mode: - return SUCCESS - - # Update action points in blackboard - blackboard.set_value("action_points", actor.action_points) - - # Check if we have enough AP - if actor.action_points >= 1: - return SUCCESS - return FAILURE diff --git a/scripts/behaviors/conditions/has_ap.gd.uid b/scripts/behaviors/conditions/has_ap.gd.uid deleted file mode 100644 index 501ee64..0000000 --- a/scripts/behaviors/conditions/has_ap.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b17qem72laaeb diff --git a/scripts/behaviors/conditions/should_move.gd b/scripts/behaviors/conditions/should_move.gd deleted file mode 100644 index 98ac474..0000000 --- a/scripts/behaviors/conditions/should_move.gd +++ /dev/null @@ -1,27 +0,0 @@ -extends ConditionLeaf - -func tick(actor: Node, blackboard: Blackboard) -> int: - # Find a valid movement target - var target_pos = find_valid_target(actor) - if target_pos: - # Store target in blackboard - blackboard.set_value("move_target", target_pos) - return SUCCESS - return FAILURE - -func find_valid_target(actor: Node) -> Vector2i: - # Get random position in range - var valid_positions = [] - - for x in range(max(0, actor.current_position.x - actor.movement_range), - min(actor.enhanced_gridmap.columns, actor.current_position.x + actor.movement_range + 1)): - for z in range(max(0, actor.current_position.y - actor.movement_range), - min(actor.enhanced_gridmap.rows, actor.current_position.y + actor.movement_range + 1)): - var pos = Vector2i(x, z) - if pos != actor.current_position and actor.is_within_movement_range(pos): - if not actor.is_position_occupied(pos): - valid_positions.append(pos) - - if valid_positions.size() > 0: - return valid_positions[randi() % valid_positions.size()] - return Vector2i(-1, -1) diff --git a/scripts/behaviors/conditions/should_move.gd.uid b/scripts/behaviors/conditions/should_move.gd.uid deleted file mode 100644 index d050061..0000000 --- a/scripts/behaviors/conditions/should_move.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bc7jpc1bwy4dg diff --git a/scripts/bot_behavior.gd b/scripts/bot_behavior.gd deleted file mode 100644 index 991b40f..0000000 --- a/scripts/bot_behavior.gd +++ /dev/null @@ -1,28 +0,0 @@ -extends BeehaveTree - -func _ready(): - if Engine.is_editor_hint(): - return - - # Get parent node safely - var parent = get_parent() - if not parent: - push_error("BehaviorTree: No parent node found") - return - - # Only setup for bots - if not parent.is_in_group("Bots"): - queue_free() # Remove tree if not a bot - return - - # Set this tree's actor - actor = parent - - enabled = parent.is_multiplayer_authority() and parent.is_bot - - # Set up blackboard with initial values - if blackboard: - blackboard.set_value("action_points", parent.action_points) - blackboard.set_value("current_action", "idle") - blackboard.set_value("grab_position", parent.current_position) # Default grab position - blackboard.set_value("move_target", null) diff --git a/scripts/bot_behavior.gd.uid b/scripts/bot_behavior.gd.uid deleted file mode 100644 index ce979c9..0000000 --- a/scripts/bot_behavior.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://6g75rh3nj2s6 diff --git a/scripts/bot_blackboard.gd b/scripts/bot_blackboard.gd deleted file mode 100644 index f85c011..0000000 --- a/scripts/bot_blackboard.gd +++ /dev/null @@ -1,19 +0,0 @@ -@tool -extends Blackboard - -var default_data = { - "move_target": null, - "current_action": "", - "action_points": 3, # Adjust this value based on your game design - "last_position": null, - "path": [], - "is_moving": false -} - -func _ready(): - if Engine.is_editor_hint(): - return - - for key in default_data: - if not has_value(key): - set_value(key, default_data[key]) diff --git a/scripts/bot_blackboard.gd.uid b/scripts/bot_blackboard.gd.uid deleted file mode 100644 index f74dff7..0000000 --- a/scripts/bot_blackboard.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d2cr28ak2s1rr diff --git a/scripts/bot_controller.gd b/scripts/bot_controller.gd new file mode 100644 index 0000000..9f689f8 --- /dev/null +++ b/scripts/bot_controller.gd @@ -0,0 +1,495 @@ +extends Node +class_name BotController + +# BotController - Standalone modular bot AI system (no Beehave dependency) +# Handles all bot decision-making: movement, grabbing, putting, arranging, and sabotage + +# Configuration +@export var tick_rate: int = 60 # Ticks between AI updates (in frames) +@export var action_delay: float = 0.5 # Delay between actions + +# References +var actor: Node3D # The player character this controller is attached to +var enhanced_gridmap: Node +var strategic_planner: RefCounted + +# State tracking +var _tick_counter: int = 0 +var _is_processing_action: bool = false +var _current_action: String = "idle" + +# Tile constants +const GOAL_TILES = [7, 8, 9, 10] +const HOLO_TILES = [11, 12, 13, 14] + +func _ready(): + # print("[BotController] _ready called for parent: ", get_parent().name) + if Engine.is_editor_hint(): + return + + # Get parent (should be player character) + actor = get_parent() + if not actor: + push_error("[BotController] No parent node found") + queue_free() + return + + # Only run for bots + # DEBUG: Print exact state of checks + # print("[BotController] Checking if %s is bot. in_group(Bots): %s, is_bot: %s" % [actor.name, actor.is_in_group("Bots"), actor.get("is_bot")]) + + if not actor.is_in_group("Bots") and not actor.get("is_bot"): + # print("[BotController] Actor is not a bot, removing controller.") + queue_free() + return + + # Wait for actor to be fully ready + await get_tree().create_timer(1.0).timeout # Increased wait time to 1.0s to be safe + + enhanced_gridmap = actor.enhanced_gridmap + if not enhanced_gridmap: + push_error("[BotController] EnhancedGridMap not found for " + actor.name) + return # Don't crash, just stop + + # Initialize strategic planner + var BotStrategicPlanner = load("res://scripts/bot_strategic_planner.gd") + strategic_planner = BotStrategicPlanner.new(actor, enhanced_gridmap) + + # Disable input processing for bots + actor.set_process_input(false) + actor.set_process_unhandled_input(false) + + print("[BotController] SUCCESFULLY STARTED for bot: %s (Authority: %s)" % [actor.name, actor.get_multiplayer_authority()]) + +func _exit_tree(): + # Ensure explicit cleanup to assist RefCounted cycle breaking + strategic_planner = null + actor = null + enhanced_gridmap = null + +func _physics_process(_delta): + if not is_instance_valid(actor) or not strategic_planner: + return + + # Only run on server/authority (Authority 1) + # NOTE: If we are not the server, we should not run logic + if not multiplayer.is_server(): + return + + # Rate limiting + _tick_counter += 1 + if _tick_counter < tick_rate: + return + _tick_counter = 0 + + # Don't process if already doing something + if _is_processing_action: + return + + # Run AI decision loop + print("[BotController] Running AI Tick for ", actor.name) + _run_ai_tick() + +func _run_ai_tick(): + """Main AI decision loop - replaces Beehave behavior tree.""" + if not is_instance_valid(actor) or _is_processing_action: + return + + print("[BotController] AI Tick: evaluating priorities...") + + # Priority 1: Use power-up sabotage if conditions are met + if await _try_use_powerup(): + print("[BotController] Action Taken: PowerUp") + return + + # Priority 2: Grab tiles (goal tiles or holo tiles) + if await _try_grab(): + print("[BotController] Action Taken: Grab") + return + + # Priority 3: Move toward needed tiles + if await _try_move(): + print("[BotController] Action Taken: Move") + return + + # Priority 4: Put tiles back on grid (rarely needed) + if await _try_put(): + print("[BotController] Action Taken: Put") + return + + # Priority 5: Arrange playerboard + if await _try_arrange(): + print("[BotController] Action Taken: Arrange") + return + + print("[BotController] No action taken (Idle). AP: %d, GoalsAchieved: %s" % [actor.action_points, _is_goals_achieved()]) + +# ============================================================================= +# Power-Up / Sabotage +# ============================================================================= + +func _try_use_powerup() -> bool: + """Check and execute power-up sabotage.""" + var powerup_manager = actor.get_node_or_null("PowerUpManager") + if not powerup_manager or not powerup_manager.can_use_special(): + return false + + # Evaluate sabotage opportunity + var eval = strategic_planner.evaluate_sabotage_opportunity() + if not eval.should_sabotage: + return false + + # Execute sabotage + _is_processing_action = true + _current_action = "sabotaging" + + var success = powerup_manager.use_special_effect() + if success: + print("[BotController] %s used power-up (reason: %s)" % [actor.name, eval.reason]) + var main = get_tree().get_root().get_node_or_null("Main") + if main and main.has_method("broadcast_message"): + main.rpc("broadcast_message", actor.display_name, "Used a special power!") + + await get_tree().create_timer(action_delay).timeout + if not is_instance_valid(self): return true # Early exit if deleted + + _is_processing_action = false + _current_action = "idle" + return success + +# ============================================================================= +# Grab Tiles +# ============================================================================= + +func _try_grab() -> bool: + """Try to grab a tile from the grid.""" + # Check AP only if turn-based + if TurnManager.turn_based_mode and actor.action_points <= 0: + return false + + if _is_playerboard_full(): + # print("[BotController] Grab failed: Board full") + return false + + # Check if goals already achieved + if _is_goals_achieved(): + return false + + # Get tiles we need + var tiles_needed = strategic_planner.get_tiles_needed() + + # Check current position and adjacent cells for tiles + var grab_info = _find_tile_to_grab(tiles_needed) + if not grab_info.position: + # print("[BotController] Grab failed: No valid tile found in range") + return false + + # Execute grab + _is_processing_action = true + _current_action = "grabbing" + + var cell = Vector3i(grab_info.position.x, 1, grab_info.position.y) + var item = enhanced_gridmap.get_cell_item(cell) + + # Handle holo tiles (power-up) + if item in HOLO_TILES: + if actor.is_multiplayer_authority(): + actor.rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) + var powerup_manager = actor.get_node_or_null("PowerUpManager") + if powerup_manager: + powerup_manager.add_holo_pickup() + + # Only consume AP in turn-based mode + if TurnManager.turn_based_mode: + actor.action_points -= 1 + + print("[BotController] %s collected holo tile!" % actor.name) + else: + # Regular tile - place in playerboard + var target_slot = strategic_planner.find_best_slot_for_tile(item) + if target_slot != -1 and actor.is_multiplayer_authority(): + actor.playerboard[target_slot] = item + actor.rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) + actor.rpc("sync_playerboard", actor.playerboard) + + if TurnManager.turn_based_mode: + actor.action_points -= 1 + + print("[BotController] %s grabbed tile %d -> slot %d" % [actor.name, item, target_slot]) + + # Check goal completion + if _is_goals_achieved(): + _handle_goal_completion() + + await get_tree().create_timer(action_delay).timeout + if not is_instance_valid(self): return true + _is_processing_action = false + _current_action = "idle" + return true + +func _find_tile_to_grab(tiles_needed: Array) -> Dictionary: + """Find best tile to grab from current or adjacent positions.""" + var result = {"position": null, "type": - 1} + + # Check current position first + var current_cell = Vector3i(actor.current_position.x, 1, actor.current_position.y) + var item = enhanced_gridmap.get_cell_item(current_cell) + + # Priority: needed tiles > holo tiles > any goal tile + if item in tiles_needed: + return {"position": actor.current_position, "type": item} + if item in HOLO_TILES: + result = {"position": actor.current_position, "type": item} + + # Check adjacent cells + var neighbors = enhanced_gridmap.get_neighbors(actor.current_position, 0) + + for neighbor in neighbors: + if not neighbor.is_walkable: + continue + var cell = Vector3i(neighbor.position.x, 1, neighbor.position.y) + item = enhanced_gridmap.get_cell_item(cell) + + if item in tiles_needed: + return {"position": neighbor.position, "type": item} + + if item in HOLO_TILES and not result.position: + result = {"position": neighbor.position, "type": item} + + # Third pass: any goal tile + if not result.position: + for neighbor in neighbors: + if not neighbor.is_walkable: + continue + var cell = Vector3i(neighbor.position.x, 1, neighbor.position.y) + item = enhanced_gridmap.get_cell_item(cell) + if item in actor.goals and item != -1: + return {"position": neighbor.position, "type": item} + + return result + +# ============================================================================= +# Movement +# ============================================================================= + +func _try_move() -> bool: + """Try to move toward needed tiles.""" + if TurnManager.turn_based_mode and actor.action_points <= 0: + return false + + if _is_goals_achieved(): + return false + + # Find optimal movement target + var target_pos = strategic_planner.find_optimal_move_target() + + if target_pos == Vector2i(-1, -1) or target_pos == actor.current_position: + print("[BotController] Move failed: No valid target or already at target. Pos: %s" % actor.current_position) + return false + + # Check if within movement range + if not actor.is_within_movement_range(target_pos): + print("[BotController] Move failed: Target %s out of range" % target_pos) + return false + + # Execute movement + _is_processing_action = true + _current_action = "moving" + + if actor.is_multiplayer_authority(): + var path = enhanced_gridmap.find_path( + Vector2(actor.current_position), + Vector2(target_pos), + 0, + false + ) + if path.size() > 1: + path.pop_front() + actor.rpc("start_movement_along_path", path, false) + + if TurnManager.turn_based_mode: + actor.action_points -= 1 + + var tiles_needed = strategic_planner.get_tiles_needed() + print("[BotController] %s moving toward tiles %s (Target: %s)" % [actor.name, tiles_needed, target_pos]) + + await get_tree().create_timer(action_delay * 2).timeout # Movement takes longer + if not is_instance_valid(self): return true + _is_processing_action = false + _current_action = "idle" + return true + +# ============================================================================= +# Put Tiles Back +# ============================================================================= + +func _try_put() -> bool: + """Try to put a tile from playerboard onto grid.""" + if actor.action_points <= 0: + return false + + # Find a tile in playerboard that we could put + var put_slot = -1 + for i in range(actor.playerboard.size()): + if actor.playerboard[i] in GOAL_TILES: + put_slot = i + break + + if put_slot == -1: + return false + + # Find empty adjacent cell + var put_position = _find_empty_adjacent_cell() + if not put_position: + return false + + # Execute put + _is_processing_action = true + _current_action = "putting" + + if actor.is_multiplayer_authority(): + var item = actor.playerboard[put_slot] + var cell = Vector3i(put_position.x, 1, put_position.y) + actor.rpc("sync_grid_item", cell.x, cell.y, cell.z, item) + actor.playerboard[put_slot] = -1 + actor.rpc("sync_playerboard", actor.playerboard) + actor.action_points -= 1 + print("[BotController] %s put tile %d at %s" % [actor.name, item, put_position]) + + await get_tree().create_timer(action_delay).timeout + if not is_instance_valid(self): return true + _is_processing_action = false + _current_action = "idle" + return true + +func _find_empty_adjacent_cell() -> Vector2i: + """Find an empty cell adjacent to player.""" + var current_cell = Vector3i(actor.current_position.x, 1, actor.current_position.y) + if enhanced_gridmap.get_cell_item(current_cell) == -1: + return actor.current_position + + var neighbors = enhanced_gridmap.get_neighbors(actor.current_position, 0) + for neighbor in neighbors: + if not neighbor.is_walkable: + continue + var cell = Vector3i(neighbor.position.x, 1, neighbor.position.y) + if enhanced_gridmap.get_cell_item(cell) == -1: + return neighbor.position + + return Vector2i(-1, -1) + +# ============================================================================= +# Arrange Playerboard +# ============================================================================= + +func _try_arrange() -> bool: + """Try to rearrange tiles in playerboard.""" + if actor.action_points < 2: + return false + + if _is_goals_achieved(): + return false + + # Find misplaced item and better position + var arrangement = _find_best_arrangement() + if arrangement.is_empty(): + return false + + # Execute arrangement + _is_processing_action = true + _current_action = "arranging" + + if actor.is_multiplayer_authority(): + var item = actor.playerboard[arrangement.source_slot] + actor.playerboard[arrangement.target_slot] = item + actor.playerboard[arrangement.source_slot] = -1 + actor.rpc("sync_playerboard", actor.playerboard) + actor.action_points -= 2 + print("[BotController] %s arranged slot %d -> %d" % [actor.name, arrangement.source_slot, arrangement.target_slot]) + + await get_tree().create_timer(action_delay).timeout + if not is_instance_valid(self): return true + _is_processing_action = false + _current_action = "idle" + return true + +func _find_best_arrangement() -> Dictionary: + """Find a tile that can be moved to a better position.""" + for i in range(1, 4): # Check central 3x3 + for j in range(1, 4): + var board_idx = i * 5 + j + var goal_idx = (i - 1) * 3 + (j - 1) + + var current_item = actor.playerboard[board_idx] + var goal_item = actor.goals[goal_idx] if goal_idx < actor.goals.size() else -1 + + if current_item != goal_item and current_item != -1: + # Find better position for this item + for gi in range(3): + for gj in range(3): + if actor.goals[gi * 3 + gj] == current_item: + var target_slot = (gi + 1) * 5 + (gj + 1) + if actor.playerboard[target_slot] == -1: + return {"source_slot": board_idx, "target_slot": target_slot} + + return {} + +# ============================================================================= +# Utility Functions +# ============================================================================= + +func _is_playerboard_full() -> bool: + for item in actor.playerboard: + if item == -1: + return false + return true + +func _is_goals_achieved() -> bool: + """Check if goal pattern is complete in any 3x3 region of playerboard.""" + var goals_2d = [] + for i in range(3): + var row = [] + for j in range(3): + row.append(actor.goals[i * 3 + j] if i * 3 + j < actor.goals.size() else -1) + goals_2d.append(row) + + var board_2d = [] + for i in range(5): + var row = [] + for j in range(5): + row.append(actor.playerboard[i * 5 + j] if i * 5 + j < actor.playerboard.size() else -1) + board_2d.append(row) + + for start_row in range(3): + for start_col in range(3): + var matches = true + for i in range(3): + for j in range(3): + var board_item = board_2d[start_row + i][start_col + j] + var goal_item = goals_2d[i][j] + if goal_item != -1 and goal_item != board_item: + matches = false + break + elif goal_item == -1 and board_item != -1: + matches = false + break + if not matches: + break + if matches: + return true + return false + +func _handle_goal_completion(): + """Handle goal completion - trigger scoring.""" + var main = get_tree().get_root().get_node_or_null("Main") + if main: + var goals_cycle_manager = main.get_node_or_null("GoalsCycleManager") + if goals_cycle_manager: + var time_remaining = goals_cycle_manager.get_time_remaining() + goals_cycle_manager.on_goal_completed(actor, time_remaining) + + var powerup_manager = actor.get_node_or_null("PowerUpManager") + if powerup_manager: + powerup_manager.add_goal_completion_reward() + + print("[BotController] %s COMPLETED GOAL!" % actor.name) diff --git a/scripts/bot_controller.gd.uid b/scripts/bot_controller.gd.uid new file mode 100644 index 0000000..a2b4a71 --- /dev/null +++ b/scripts/bot_controller.gd.uid @@ -0,0 +1 @@ +uid://cwwwixc07jc86 diff --git a/scripts/bot_strategic_planner.gd b/scripts/bot_strategic_planner.gd new file mode 100644 index 0000000..9e9595f --- /dev/null +++ b/scripts/bot_strategic_planner.gd @@ -0,0 +1,318 @@ +extends RefCounted +class_name BotStrategicPlanner + +# BotStrategicPlanner - Strategic decision-making for bot AI +# Evaluates tile needs, pathfinding targets, and sabotage opportunities + +var actor: Node3D +var enhanced_gridmap: Node + +# Tile type constants +const GOAL_TILES = [7, 8, 9, 10] # Heart, Diamond, Star, Coin +const HOLO_TILES = [11, 12, 13, 14] # Power-up holo tiles + +func _init(p_actor: Node3D, p_gridmap: Node): + actor = p_actor + enhanced_gridmap = p_gridmap + +# ============================================================================= +# Goal Analysis +# ============================================================================= + +func calculate_goal_progress() -> float: + """Returns 0.0-1.0 representing how close bot is to completing their goal.""" + if not actor or actor.goals.size() == 0: + return 0.0 + + var matches = 0 + var total_required = 0 + + # Check center 3x3 of playerboard against goals + for i in range(3): + for j in range(3): + var goal_idx = i * 3 + j + if goal_idx >= actor.goals.size(): + continue + + var goal_value = actor.goals[goal_idx] + if goal_value == -1: + continue # Empty goal slot + + total_required += 1 + var board_idx = (i + 1) * 5 + (j + 1) # Center 3x3 in 5x5 board + if board_idx < actor.playerboard.size() and actor.playerboard[board_idx] == goal_value: + matches += 1 + + if total_required == 0: + return 1.0 # All goals are -1 (empty) + + return float(matches) / float(total_required) + +func get_tiles_needed() -> Array: + """Returns array of tile types still needed to complete goal.""" + var needed = [] + + if not actor or actor.goals.size() == 0: + return needed + + for i in range(3): + for j in range(3): + var goal_idx = i * 3 + j + if goal_idx >= actor.goals.size(): + continue + + var goal_value = actor.goals[goal_idx] + if goal_value == -1: + continue + + var board_idx = (i + 1) * 5 + (j + 1) + if board_idx >= actor.playerboard.size() or actor.playerboard[board_idx] != goal_value: + if not goal_value in needed: + needed.append(goal_value) + + return needed + +func find_best_slot_for_tile(tile_type: int) -> int: + """Find the best playerboard slot for a given tile type.""" + # Check goals to find matching position + for i in range(3): + for j in range(3): + var goal_idx = i * 3 + j + if goal_idx < actor.goals.size() and actor.goals[goal_idx] == tile_type: + var board_idx = (i + 1) * 5 + (j + 1) + if board_idx < actor.playerboard.size() and actor.playerboard[board_idx] == -1: + return board_idx + + # Fallback: any empty slot + return actor.playerboard.find(-1) + +# ============================================================================= +# Tile Finding +# ============================================================================= + +func find_best_tile_to_grab() -> Dictionary: + """Find the best tile to grab, prioritizing goal tiles then holo tiles.""" + var needed_tiles = get_tiles_needed() + var best_tile = {"position": null, "type": - 1, "priority": 0} + + if not enhanced_gridmap: + return best_tile + + # Search nearby area for tiles + var search_radius = 5 + var current_pos = actor.current_position + + for dx in range(-search_radius, search_radius + 1): + for dz in range(-search_radius, search_radius + 1): + var pos = Vector2i(current_pos.x + dx, current_pos.y + dz) + if not enhanced_gridmap.is_position_valid(pos): + continue + + var cell = Vector3i(pos.x, 1, pos.y) + var item = enhanced_gridmap.get_cell_item(cell) + + if item == -1: + continue + + var priority = 0 + + # Priority 1: Tiles we need for goals + if item in needed_tiles: + priority = 10 - abs(dx) - abs(dz) # Closer = higher priority + # Priority 2: Holo tiles for power-ups + elif item in HOLO_TILES: + priority = 5 - abs(dx) - abs(dz) + elif item in GOAL_TILES: + priority = 1 # Low priority - might be useful later + + if priority > best_tile.priority: + best_tile = {"position": pos, "type": item, "priority": priority} + + return best_tile + +func find_nearest_tile_of_type(tile_types: Array) -> Vector2i: + """Find nearest tile matching any type in array.""" + var current_pos = actor.current_position + var nearest_pos = Vector2i(-1, -1) + var nearest_dist = 999999 + + if not enhanced_gridmap: + return nearest_pos + + for x in range(enhanced_gridmap.columns): + for z in range(enhanced_gridmap.rows): + var pos = Vector2i(x, z) + var cell = Vector3i(x, 1, z) + var item = enhanced_gridmap.get_cell_item(cell) + + if item in tile_types: + var dist = abs(pos.x - current_pos.x) + abs(pos.y - current_pos.y) + if dist < nearest_dist: + nearest_dist = dist + nearest_pos = pos + + return nearest_pos + +# ============================================================================= +# Movement Strategy +# ============================================================================= + +func find_optimal_move_target() -> Vector2i: + """Calculate the best position to move towards.""" + var needed_tiles = get_tiles_needed() + + # First: move toward tiles we need + if needed_tiles.size() > 0: + var target = find_nearest_tile_of_type(needed_tiles) + if target != Vector2i(-1, -1): + return _get_adjacent_position(target) + + # Second: move toward holo tiles if we need power-ups + var powerup_manager = actor.get_node_or_null("PowerUpManager") + if powerup_manager and powerup_manager.current_points < powerup_manager.MAX_POINTS: + var target = find_nearest_tile_of_type(HOLO_TILES) + if target != Vector2i(-1, -1): + return _get_adjacent_position(target) + + # Third: move toward any goal tile that might be useful + var target = find_nearest_tile_of_type(GOAL_TILES) + if target != Vector2i(-1, -1): + return _get_adjacent_position(target) + + # Fallback: random valid position + return _get_random_valid_position() + +func _get_adjacent_position(target: Vector2i) -> Vector2i: + """Get a valid position adjacent to or at the target.""" + var current_pos = actor.current_position + + # If we can reach the target directly, return it + if _is_within_movement_range(target): + return target + + # Otherwise, move one step closer + var dx = sign(target.x - current_pos.x) + var dz = sign(target.y - current_pos.y) + + var positions_to_try = [ + Vector2i(current_pos.x + dx, current_pos.y + dz), + Vector2i(current_pos.x + dx, current_pos.y), + Vector2i(current_pos.x, current_pos.y + dz) + ] + + for pos in positions_to_try: + if _is_valid_move_target(pos): + return pos + + return Vector2i(-1, -1) + +func _is_within_movement_range(pos: Vector2i) -> bool: + var current_pos = actor.current_position + var dist = max(abs(pos.x - current_pos.x), abs(pos.y - current_pos.y)) + return dist <= actor.movement_range + +func _is_valid_move_target(pos: Vector2i) -> bool: + if not enhanced_gridmap or not enhanced_gridmap.is_position_valid(pos): + return false + if actor.is_position_occupied(pos): + return false + return true + +func _get_random_valid_position() -> Vector2i: + var valid_positions = [] + var current_pos = actor.current_position + var range_val = actor.movement_range + + for dx in range(-range_val, range_val + 1): + for dz in range(-range_val, range_val + 1): + if dx == 0 and dz == 0: + continue + var pos = Vector2i(current_pos.x + dx, current_pos.y + dz) + if _is_valid_move_target(pos): + valid_positions.append(pos) + + if valid_positions.size() > 0: + return valid_positions[randi() % valid_positions.size()] + return Vector2i(-1, -1) + +# ============================================================================= +# Sabotage Strategy +# ============================================================================= + +func evaluate_sabotage_opportunity() -> Dictionary: + """Evaluate whether to use power-up for sabotage.""" + var result = {"should_sabotage": false, "reason": "", "target": null} + + var powerup_manager = actor.get_node_or_null("PowerUpManager") + if not powerup_manager or not powerup_manager.can_use_special(): + return result + + # Get opponents + var opponents = _get_opponents() + if opponents.size() == 0: + return result + + # Check conditions for sabotage (balanced strategy) + + # Condition 1: Power-ups are maxed - use it or lose potential gains + if powerup_manager.current_points >= powerup_manager.MAX_POINTS: + result.should_sabotage = true + result.reason = "max_powerup" + result.target = opponents[randi() % opponents.size()] + return result + + # Condition 2: Opponent is close to completing their goal + for opponent in opponents: + var opponent_progress = _estimate_opponent_progress(opponent) + if opponent_progress >= 0.7: # 70% complete + result.should_sabotage = true + result.reason = "opponent_close_to_winning" + result.target = opponent + return result + + # Condition 3: Bot is behind in score - need to catch up + var goals_cycle_manager = actor.get_tree().get_root().get_node_or_null("Main/GoalsCycleManager") + if goals_cycle_manager: + var leaderboard = goals_cycle_manager.get_leaderboard() + var my_rank = _get_rank_in_leaderboard(leaderboard) + if my_rank > 1 and powerup_manager.get_bars() >= 2: + result.should_sabotage = true + result.reason = "behind_in_score" + result.target = opponents[0] if opponents.size() > 0 else null + return result + + return result + +func _get_opponents() -> Array: + var all_players = actor.get_tree().get_nodes_in_group("Players") + return all_players.filter(func(p): return p != actor) + +func _estimate_opponent_progress(opponent: Node) -> float: + """Estimate opponent's goal progress based on their playerboard.""" + if not opponent or opponent.goals.size() == 0: + return 0.0 + + var matches = 0 + var total = 0 + + for i in range(3): + for j in range(3): + var goal_idx = i * 3 + j + if goal_idx >= opponent.goals.size(): + continue + var goal_value = opponent.goals[goal_idx] + if goal_value == -1: + continue + total += 1 + var board_idx = (i + 1) * 5 + (j + 1) + if board_idx < opponent.playerboard.size() and opponent.playerboard[board_idx] == goal_value: + matches += 1 + + return float(matches) / float(max(total, 1)) + +func _get_rank_in_leaderboard(leaderboard: Array) -> int: + var my_id = actor.get_multiplayer_authority() + for i in range(leaderboard.size()): + if leaderboard[i].get("peer_id", -1) == my_id: + return i + 1 + return leaderboard.size() + 1 diff --git a/scripts/bot_strategic_planner.gd.uid b/scripts/bot_strategic_planner.gd.uid new file mode 100644 index 0000000..b522b5e --- /dev/null +++ b/scripts/bot_strategic_planner.gd.uid @@ -0,0 +1 @@ +uid://q4lnha3plt3w diff --git a/scripts/managers/game_state_manager.gd b/scripts/managers/game_state_manager.gd index 66b983d..4445969 100644 --- a/scripts/managers/game_state_manager.gd +++ b/scripts/managers/game_state_manager.gd @@ -5,7 +5,7 @@ extends Node signal game_started() signal game_state_changed() -@export var enable_bots: bool = false +@export var enable_bots: bool = true @export var max_players: int = 4 var players: Array = [] diff --git a/scripts/managers/goals_cycle_manager.gd b/scripts/managers/goals_cycle_manager.gd index db08ef5..0a69159 100644 --- a/scripts/managers/goals_cycle_manager.gd +++ b/scripts/managers/goals_cycle_manager.gd @@ -140,7 +140,8 @@ func _initialize_player_scores(): """Initialize scores for all connected players to 0.""" var all_players = get_tree().get_nodes_in_group("Players") for player in all_players: - var peer_id = player.get_multiplayer_authority() + # Use name.to_int() for ID because bots share authority 1 but have unique node names + var peer_id = player.name.to_int() if not player_scores.has(peer_id): player_scores[peer_id] = 0 _update_leaderboard() @@ -186,7 +187,8 @@ func on_goal_completed(player: Node, time_remaining: float): if not multiplayer.is_server(): return - var peer_id = player.get_multiplayer_authority() + # Use name.to_int() for ID because bots share authority 1 + var peer_id = player.name.to_int() # Calculate score: base + time bonus var time_bonus = int(time_remaining * TIME_BONUS_MULTIPLIER) @@ -241,7 +243,8 @@ func _process_cycle_end_for_all_players(): var all_players = get_tree().get_nodes_in_group("Players") for player in all_players: - var peer_id = player.get_multiplayer_authority() + # Use name.to_int() for ID because bots share authority 1 + var peer_id = player.name.to_int() var match_score = _calculate_match_score(player) if match_score > 0: @@ -297,7 +300,7 @@ func regenerate_goals_for_player(player: Node): player.goals = int_goals # Use main scene's RPC which properly looks up player by ID on each client - var peer_id = player.get_multiplayer_authority() + var peer_id = player.name.to_int() if main_scene: main_scene.rpc("sync_player_goals", peer_id, int_goals) diff --git a/scripts/managers/player_action_manager.gd b/scripts/managers/player_action_manager.gd index 3e1e611..61e81b6 100644 --- a/scripts/managers/player_action_manager.gd +++ b/scripts/managers/player_action_manager.gd @@ -47,17 +47,22 @@ func after_action_completed(): # Clear the highlights after placing the tiles. (Quickfix for Clientside) clear_highlights() + # Only update UI if this is the LOCAL HUMAN PLAYER + # Bots are owned by the host (authority match) but shouldn't trigger UI updates if multiplayer.get_unique_id() == player.get_multiplayer_authority(): - var main = player.get_tree().get_root().get_node_or_null("Main") - if main: - main.ui_manager.update_button_states() - main.ui_manager.update_playerboard_ui() - - # Add this line to sync all boards - main.update_all_players_boards() - - # Add sync for playerboard - if player.is_multiplayer_authority(): + if not player.is_bot and not player.is_in_group("Bots"): + var main = player.get_tree().get_root().get_node_or_null("Main") + if main: + main.ui_manager.update_button_states() + main.ui_manager.update_playerboard_ui() + + # Add this line to sync all boards + main.update_all_players_boards() + + # Sync playerboard (Bots DO need to sync their board logic, just not update local UI) + if player.is_multiplayer_authority(): + var main = player.get_tree().get_root().get_node_or_null("Main") + if main: main.rpc("sync_playerboard", player.get_multiplayer_authority(), player.playerboard) player._is_processing_action = false