From f2da71a00339e6c181de4cba36dec854289eb051 Mon Sep 17 00:00:00 2001 From: chendian <-> Date: Mon, 30 Oct 2023 00:34:13 +0800 Subject: [PATCH] ai --- addons/beehave/LICENSE | 21 ++ addons/beehave/blackboard.gd | 33 +++ addons/beehave/debug/debugger.gd | 91 ++++++ addons/beehave/debug/debugger_messages.gd | 31 +++ addons/beehave/debug/debugger_tab.gd | 107 ++++++++ addons/beehave/debug/frames.gd | 33 +++ addons/beehave/debug/global_debugger.gd | 38 +++ addons/beehave/debug/graph_edit.gd | 259 ++++++++++++++++++ addons/beehave/debug/graph_node.gd | 145 ++++++++++ .../beehave/debug/icons/horizontal_layout.svg | 1 + .../debug/icons/horizontal_layout.svg.import | 38 +++ addons/beehave/debug/icons/port_bottom.svg | 1 + .../debug/icons/port_bottom.svg.import | 38 +++ addons/beehave/debug/icons/port_left.svg | 1 + .../beehave/debug/icons/port_left.svg.import | 38 +++ addons/beehave/debug/icons/port_right.svg | 1 + .../beehave/debug/icons/port_right.svg.import | 38 +++ addons/beehave/debug/icons/port_top.svg | 1 + .../beehave/debug/icons/port_top.svg.import | 38 +++ .../beehave/debug/icons/vertical_layout.svg | 1 + .../debug/icons/vertical_layout.svg.import | 38 +++ addons/beehave/debug/tree_node.gd | 255 +++++++++++++++++ addons/beehave/icons/action.svg | 38 +++ addons/beehave/icons/action.svg.import | 38 +++ addons/beehave/icons/blackboard.svg | 38 +++ addons/beehave/icons/blackboard.svg.import | 38 +++ addons/beehave/icons/category_bt.svg | 38 +++ addons/beehave/icons/category_bt.svg.import | 38 +++ addons/beehave/icons/category_composite.svg | 38 +++ .../icons/category_composite.svg.import | 38 +++ addons/beehave/icons/category_decorator.svg | 38 +++ .../icons/category_decorator.svg.import | 38 +++ addons/beehave/icons/category_leaf.svg | 38 +++ addons/beehave/icons/category_leaf.svg.import | 38 +++ addons/beehave/icons/condition.svg | 38 +++ addons/beehave/icons/condition.svg.import | 38 +++ addons/beehave/icons/failer.svg | 38 +++ addons/beehave/icons/failer.svg.import | 38 +++ addons/beehave/icons/inverter.svg | 38 +++ addons/beehave/icons/inverter.svg.import | 38 +++ addons/beehave/icons/limiter.svg | 38 +++ addons/beehave/icons/limiter.svg.import | 38 +++ addons/beehave/icons/selector.svg | 38 +++ addons/beehave/icons/selector.svg.import | 38 +++ addons/beehave/icons/selector_random.svg | 35 +++ .../beehave/icons/selector_random.svg.import | 38 +++ addons/beehave/icons/selector_reactive.svg | 45 +++ .../icons/selector_reactive.svg.import | 38 +++ addons/beehave/icons/sequence.svg | 38 +++ addons/beehave/icons/sequence.svg.import | 38 +++ addons/beehave/icons/sequence_random.svg | 38 +++ .../beehave/icons/sequence_random.svg.import | 38 +++ addons/beehave/icons/sequence_reactive.svg | 60 ++++ .../icons/sequence_reactive.svg.import | 38 +++ addons/beehave/icons/succeeder.svg | 38 +++ addons/beehave/icons/succeeder.svg.import | 38 +++ addons/beehave/icons/tree.svg | 38 +++ addons/beehave/icons/tree.svg.import | 38 +++ .../beehave/metrics/beehave_global_metrics.gd | 54 ++++ addons/beehave/nodes/beehave_node.gd | 49 ++++ addons/beehave/nodes/beehave_tree.gd | 224 +++++++++++++++ addons/beehave/nodes/composites/composite.gd | 40 +++ .../nodes/composites/randomized_composite.gd | 152 ++++++++++ addons/beehave/nodes/composites/selector.gd | 69 +++++ .../nodes/composites/selector_random.gd | 80 ++++++ .../nodes/composites/selector_reactive.gd | 45 +++ addons/beehave/nodes/composites/sequence.gd | 76 +++++ .../nodes/composites/sequence_random.gd | 92 +++++++ .../nodes/composites/sequence_reactive.gd | 62 +++++ .../beehave/nodes/composites/sequence_star.gd | 58 ++++ addons/beehave/nodes/decorators/decorator.gd | 41 +++ addons/beehave/nodes/decorators/failer.gd | 34 +++ addons/beehave/nodes/decorators/inverter.gd | 42 +++ addons/beehave/nodes/decorators/limiter.gd | 41 +++ addons/beehave/nodes/decorators/succeeder.gd | 34 +++ .../beehave/nodes/decorators/time_limiter.gd | 46 ++++ addons/beehave/nodes/leaves/action.gd | 13 + .../nodes/leaves/blackboard_compare.gd | 61 +++++ .../beehave/nodes/leaves/blackboard_erase.gd | 25 ++ addons/beehave/nodes/leaves/blackboard_has.gd | 23 ++ addons/beehave/nodes/leaves/blackboard_set.gd | 34 +++ addons/beehave/nodes/leaves/condition.gd | 11 + addons/beehave/nodes/leaves/leaf.gd | 46 ++++ addons/beehave/plugin.cfg | 7 + addons/beehave/plugin.gd | 24 ++ addons/beehave/utils/utils.gd | 22 ++ .../main_screen/selection_actions.tscn | 4 +- .../saved_state.json | 7 +- .../typed_editors/dock_array.tscn | 4 +- .../typed_editors/dock_dict.tscn | 4 +- .../typed_editors/dock_enum_array.tscn | 4 +- config/character_move/normal.tres | 4 +- .../hero01_long_air_attack03.tres | 2 +- config/player_skill/hero01_long_attack03.tres | 2 +- config/skill/hero01.tres | 12 - config/skill/monster01_attack01.tres | 14 + project.godot | 6 +- render/mesh/slash1.obj.import | 1 - render/mesh/slash2.obj.import | 1 - render/process_material/slash_normal.tres | 10 +- render/texture/{ => shape}/explodeDecal.png | Bin .../{ => shape}/explodeDecal.png.import | 6 +- render/texture/{ => shape}/readiness_hero.png | Bin .../{ => shape}/readiness_hero.png.import | 6 +- .../texture/{ => shape}/readiness_monster.png | Bin .../{ => shape}/readiness_monster.png.import | 6 +- render/texture/{ => shape}/swordDecal.png | Bin .../texture/{ => shape}/swordDecal.png.import | 6 +- .../skill_animation/hero01_long_attack01.tres | 2 +- .../skill_animation/hero01_long_attack02.tres | 2 +- .../skill_animation/hero01_long_attack03.tres | 2 +- .../skill_animation/hero01_long_attack04.tres | 2 +- .../skill_animation/hero01_long_flash.tres | 2 +- .../skill_animation/hero01_long_skill01.tres | 2 +- .../skill_animation/monster01_attack01.tres | 43 +++ .../animation_library.tres | 6 +- scene/ai/action/find_target.tscn | 6 + scene/ai/action/move_to_target.tscn | 6 + scene/character/character.tscn | 1 + scene/character/monster.tscn | 25 +- scene/effect/afterimage/normal.tscn | 1 + scene/effect/decal/readiness_hero.tscn | 2 +- scene/effect/decal/readiness_monster.tscn | 2 +- .../particle/particle_slash_normal.tscn | 1 - scene/launcher.tscn | 15 +- script/_global/setting.gd | 2 +- script/ai/action.gd | 21 ++ script/ai/action/action_find_target.gd | 6 + script/ai/action_with_target.gd | 11 + .../action_move_to_target.gd | 12 + script/character/battle.gd | 5 +- script/character/character.gd | 2 + script/character/effect.gd | 8 +- script/character/monster/ai.gd | 8 + script/character/move.gd | 2 +- script/character/status.gd | 1 + script/character/view.gd | 8 +- script/manager/character_manager.gd | 7 + 138 files changed, 4382 insertions(+), 76 deletions(-) create mode 100644 addons/beehave/LICENSE create mode 100644 addons/beehave/blackboard.gd create mode 100644 addons/beehave/debug/debugger.gd create mode 100644 addons/beehave/debug/debugger_messages.gd create mode 100644 addons/beehave/debug/debugger_tab.gd create mode 100644 addons/beehave/debug/frames.gd create mode 100644 addons/beehave/debug/global_debugger.gd create mode 100644 addons/beehave/debug/graph_edit.gd create mode 100644 addons/beehave/debug/graph_node.gd create mode 100644 addons/beehave/debug/icons/horizontal_layout.svg create mode 100644 addons/beehave/debug/icons/horizontal_layout.svg.import create mode 100644 addons/beehave/debug/icons/port_bottom.svg create mode 100644 addons/beehave/debug/icons/port_bottom.svg.import create mode 100644 addons/beehave/debug/icons/port_left.svg create mode 100644 addons/beehave/debug/icons/port_left.svg.import create mode 100644 addons/beehave/debug/icons/port_right.svg create mode 100644 addons/beehave/debug/icons/port_right.svg.import create mode 100644 addons/beehave/debug/icons/port_top.svg create mode 100644 addons/beehave/debug/icons/port_top.svg.import create mode 100644 addons/beehave/debug/icons/vertical_layout.svg create mode 100644 addons/beehave/debug/icons/vertical_layout.svg.import create mode 100644 addons/beehave/debug/tree_node.gd create mode 100644 addons/beehave/icons/action.svg create mode 100644 addons/beehave/icons/action.svg.import create mode 100644 addons/beehave/icons/blackboard.svg create mode 100644 addons/beehave/icons/blackboard.svg.import create mode 100644 addons/beehave/icons/category_bt.svg create mode 100644 addons/beehave/icons/category_bt.svg.import create mode 100644 addons/beehave/icons/category_composite.svg create mode 100644 addons/beehave/icons/category_composite.svg.import create mode 100644 addons/beehave/icons/category_decorator.svg create mode 100644 addons/beehave/icons/category_decorator.svg.import create mode 100644 addons/beehave/icons/category_leaf.svg create mode 100644 addons/beehave/icons/category_leaf.svg.import create mode 100644 addons/beehave/icons/condition.svg create mode 100644 addons/beehave/icons/condition.svg.import create mode 100644 addons/beehave/icons/failer.svg create mode 100644 addons/beehave/icons/failer.svg.import create mode 100644 addons/beehave/icons/inverter.svg create mode 100644 addons/beehave/icons/inverter.svg.import create mode 100644 addons/beehave/icons/limiter.svg create mode 100644 addons/beehave/icons/limiter.svg.import create mode 100644 addons/beehave/icons/selector.svg create mode 100644 addons/beehave/icons/selector.svg.import create mode 100644 addons/beehave/icons/selector_random.svg create mode 100644 addons/beehave/icons/selector_random.svg.import create mode 100644 addons/beehave/icons/selector_reactive.svg create mode 100644 addons/beehave/icons/selector_reactive.svg.import create mode 100644 addons/beehave/icons/sequence.svg create mode 100644 addons/beehave/icons/sequence.svg.import create mode 100644 addons/beehave/icons/sequence_random.svg create mode 100644 addons/beehave/icons/sequence_random.svg.import create mode 100644 addons/beehave/icons/sequence_reactive.svg create mode 100644 addons/beehave/icons/sequence_reactive.svg.import create mode 100644 addons/beehave/icons/succeeder.svg create mode 100644 addons/beehave/icons/succeeder.svg.import create mode 100644 addons/beehave/icons/tree.svg create mode 100644 addons/beehave/icons/tree.svg.import create mode 100644 addons/beehave/metrics/beehave_global_metrics.gd create mode 100644 addons/beehave/nodes/beehave_node.gd create mode 100644 addons/beehave/nodes/beehave_tree.gd create mode 100644 addons/beehave/nodes/composites/composite.gd create mode 100644 addons/beehave/nodes/composites/randomized_composite.gd create mode 100644 addons/beehave/nodes/composites/selector.gd create mode 100644 addons/beehave/nodes/composites/selector_random.gd create mode 100644 addons/beehave/nodes/composites/selector_reactive.gd create mode 100644 addons/beehave/nodes/composites/sequence.gd create mode 100644 addons/beehave/nodes/composites/sequence_random.gd create mode 100644 addons/beehave/nodes/composites/sequence_reactive.gd create mode 100644 addons/beehave/nodes/composites/sequence_star.gd create mode 100644 addons/beehave/nodes/decorators/decorator.gd create mode 100644 addons/beehave/nodes/decorators/failer.gd create mode 100644 addons/beehave/nodes/decorators/inverter.gd create mode 100644 addons/beehave/nodes/decorators/limiter.gd create mode 100644 addons/beehave/nodes/decorators/succeeder.gd create mode 100644 addons/beehave/nodes/decorators/time_limiter.gd create mode 100644 addons/beehave/nodes/leaves/action.gd create mode 100644 addons/beehave/nodes/leaves/blackboard_compare.gd create mode 100644 addons/beehave/nodes/leaves/blackboard_erase.gd create mode 100644 addons/beehave/nodes/leaves/blackboard_has.gd create mode 100644 addons/beehave/nodes/leaves/blackboard_set.gd create mode 100644 addons/beehave/nodes/leaves/condition.gd create mode 100644 addons/beehave/nodes/leaves/leaf.gd create mode 100644 addons/beehave/plugin.cfg create mode 100644 addons/beehave/plugin.gd create mode 100644 addons/beehave/utils/utils.gd delete mode 100644 config/skill/hero01.tres create mode 100644 config/skill/monster01_attack01.tres rename render/texture/{ => shape}/explodeDecal.png (100%) rename render/texture/{ => shape}/explodeDecal.png.import (70%) rename render/texture/{ => shape}/readiness_hero.png (100%) rename render/texture/{ => shape}/readiness_hero.png.import (69%) rename render/texture/{ => shape}/readiness_monster.png (100%) rename render/texture/{ => shape}/readiness_monster.png.import (69%) rename render/texture/{ => shape}/swordDecal.png (100%) rename render/texture/{ => shape}/swordDecal.png.import (70%) create mode 100644 resource/skill_animation/monster01_attack01.tres create mode 100644 scene/ai/action/find_target.tscn create mode 100644 scene/ai/action/move_to_target.tscn create mode 100644 script/ai/action.gd create mode 100644 script/ai/action/action_find_target.gd create mode 100644 script/ai/action_with_target.gd create mode 100644 script/ai/action_with_target/action_move_to_target.gd create mode 100644 script/character/monster/ai.gd diff --git a/addons/beehave/LICENSE b/addons/beehave/LICENSE new file mode 100644 index 0000000..caabbff --- /dev/null +++ b/addons/beehave/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 bitbrain + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/addons/beehave/blackboard.gd b/addons/beehave/blackboard.gd new file mode 100644 index 0000000..5655e89 --- /dev/null +++ b/addons/beehave/blackboard.gd @@ -0,0 +1,33 @@ +## The blackboard is an object that can be used to store and access data between +## multiple nodes of the behavior tree. +@icon("icons/blackboard.svg") +class_name Blackboard extends Node + +var blackboard: Dictionary = {} + +func keys() -> Array[String]: + var keys: Array[String] + keys.assign(blackboard.keys().duplicate()) + return keys + + +func set_value(key: Variant, value: Variant, blackboard_name: String = 'default') -> void: + if not blackboard.has(blackboard_name): + blackboard[blackboard_name] = {} + + blackboard[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 blackboard[blackboard_name].get(key, default_value) + return default_value + + +func has_value(key: Variant, blackboard_name: String = 'default') -> bool: + return blackboard.has(blackboard_name) and blackboard[blackboard_name].has(key) and blackboard[blackboard_name][key] != null + + +func erase_value(key: Variant, blackboard_name: String = 'default') -> void: + if blackboard.has(blackboard_name): + blackboard[blackboard_name][key] = null diff --git a/addons/beehave/debug/debugger.gd b/addons/beehave/debug/debugger.gd new file mode 100644 index 0000000..1ccd097 --- /dev/null +++ b/addons/beehave/debug/debugger.gd @@ -0,0 +1,91 @@ +@tool +extends EditorDebuggerPlugin + +const DebuggerTab := preload("debugger_tab.gd") +var debugger_tab := DebuggerTab.new() +var floating_window: Window +var session: EditorDebuggerSession + + +func _has_capture(prefix: String) -> bool: + return prefix == "beehave" + + +func _capture(message: String, data: Array, session_id: int) -> bool: + # in case the behavior tree has invalid setup this might be null + if debugger_tab == null: + return false + + if message == "beehave:register_tree": + debugger_tab.register_tree(data[0]) + return true + if message == "beehave:unregister_tree": + debugger_tab.unregister_tree(data[0]) + return true + if message == "beehave:process_tick": + debugger_tab.graph.process_tick(data[0], data[1]) + return true + if message == "beehave:process_begin": + debugger_tab.graph.process_begin(data[0]) + return true + if message == "beehave:process_end": + debugger_tab.graph.process_end(data[0]) + return true + return false + + +func _setup_session(session_id: int) -> void: + session = get_session(session_id) + session.started.connect(debugger_tab.start) + session.stopped.connect(debugger_tab.stop) + + debugger_tab.name = "🐝 Beehave" + debugger_tab.make_floating.connect(_on_make_floating) + debugger_tab.session = session + session.add_session_tab(debugger_tab) + + +func _on_make_floating() -> void: + var plugin := BeehaveUtils.get_plugin() + if not plugin: + return + if floating_window: + _on_window_close_requested() + return + + var border_size := Vector2(4, 4) * BeehaveUtils.get_editor_scale() + var editor_interface: EditorInterface = plugin.get_editor_interface() + var editor_main_screen = editor_interface.get_editor_main_screen() + debugger_tab.get_parent().remove_child(debugger_tab) + + floating_window = Window.new() + + var panel := Panel.new() + panel.add_theme_stylebox_override("panel", editor_interface.get_base_control().get_theme_stylebox("PanelForeground", "EditorStyles")) + panel.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + floating_window.add_child(panel) + + var margin := MarginContainer.new() + margin.add_child(debugger_tab) + margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + margin.add_theme_constant_override("margin_right", border_size.x) + margin.add_theme_constant_override("margin_left", border_size.x) + margin.add_theme_constant_override("margin_top", border_size.y) + margin.add_theme_constant_override("margin_bottom", border_size.y) + panel.add_child(margin) + + floating_window.title = "🐝 Beehave" + floating_window.wrap_controls = true + floating_window.min_size = Vector2i(600, 350) + floating_window.size = debugger_tab.size + floating_window.position = editor_main_screen.global_position + floating_window.transient = true + floating_window.close_requested.connect(_on_window_close_requested) + editor_interface.get_base_control().add_child(floating_window) + + +func _on_window_close_requested() -> void: + debugger_tab.get_parent().remove_child(debugger_tab) + session.add_session_tab(debugger_tab) + floating_window.queue_free() + floating_window = null diff --git a/addons/beehave/debug/debugger_messages.gd b/addons/beehave/debug/debugger_messages.gd new file mode 100644 index 0000000..3fb49fb --- /dev/null +++ b/addons/beehave/debug/debugger_messages.gd @@ -0,0 +1,31 @@ +class_name BeehaveDebuggerMessages + + +static func can_send_message() -> bool: + return not Engine.is_editor_hint() and OS.has_feature("editor") + + +static func register_tree(beehave_tree: Dictionary) -> void: + if can_send_message(): + EngineDebugger.send_message("beehave:register_tree", [beehave_tree]) + + +static func unregister_tree(instance_id: int) -> void: + if can_send_message(): + EngineDebugger.send_message("beehave:unregister_tree", [instance_id]) + + +static func process_tick(instance_id: int, status: int) -> void: + if can_send_message(): + EngineDebugger.send_message("beehave:process_tick", [instance_id, status]) + + +static func process_begin(instance_id: int) -> void: + if can_send_message(): + EngineDebugger.send_message("beehave:process_begin", [instance_id]) + + +static func process_end(instance_id: int) -> void: + if can_send_message(): + EngineDebugger.send_message("beehave:process_end", [instance_id]) + diff --git a/addons/beehave/debug/debugger_tab.gd b/addons/beehave/debug/debugger_tab.gd new file mode 100644 index 0000000..3987d77 --- /dev/null +++ b/addons/beehave/debug/debugger_tab.gd @@ -0,0 +1,107 @@ +@tool +extends PanelContainer + +signal make_floating() + +const BeehaveGraphEdit := preload("graph_edit.gd") +const TREE_ICON := preload("../icons/tree.svg") + +var container: HSplitContainer +var item_list: ItemList +var graph: BeehaveGraphEdit +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 = BeehaveGraphEdit.new() + container.add_child(graph) + + message = Label.new() + message.text = "Run Project for debugging" + message.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + message.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + message.set_anchors_preset(Control.PRESET_CENTER) + add_child(message) + + var button := Button.new() + button.flat = true + button.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_zoom_hbox().add_child(button) + + var toggle_button := Button.new() + toggle_button.flat = true + 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_zoom_hbox().add_child(toggle_button) + graph.get_zoom_hbox().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: + 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 + + +func unregister_tree(instance_id: int) -> void: + var id := str(instance_id) + for i in item_list.item_count: + if item_list.get_item_metadata(i) == id: + item_list.remove_item(i) + break + + active_trees.erase(id) + + if graph.beehave_tree.get("id", "") == id: + graph.beehave_tree = {} + + +func _on_toggle_button_pressed(toggle_button: Button) -> void: + item_list.visible = !item_list.visible + toggle_button.icon = get_theme_icon(&"Back" if item_list.visible else &"Forward", &"EditorIcons") + + +func _on_item_selected(idx: int) -> void: + var id: StringName = item_list.get_item_metadata(idx) + graph.beehave_tree = active_trees.get(id, {}) + + active_tree_id = id.to_int() + session.send_message("beehave:activate_tree", [active_tree_id]) + + +func _on_visibility_changed() -> void: + session.send_message("beehave:visibility_changed", [visible and is_visible_in_tree()]) diff --git a/addons/beehave/debug/frames.gd b/addons/beehave/debug/frames.gd new file mode 100644 index 0000000..0fb6d2a --- /dev/null +++ b/addons/beehave/debug/frames.gd @@ -0,0 +1,33 @@ +@tool +extends RefCounted + +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/global_debugger.gd b/addons/beehave/debug/global_debugger.gd new file mode 100644 index 0000000..27c72c2 --- /dev/null +++ b/addons/beehave/debug/global_debugger.gd @@ -0,0 +1,38 @@ +extends Node + +var _registered_trees: Dictionary +var _active_tree: BeehaveTree + + +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: + _active_tree._can_send_message = data[0] + return true + return false + + +func _set_active_tree(tree_id: int) -> void: + var tree: BeehaveTree = _registered_trees.get(tree_id, null) + if not tree: + return + + if _active_tree: + _active_tree._can_send_message = false + _active_tree = tree + _active_tree._can_send_message = true + + +func register_tree(tree: BeehaveTree) -> void: + _registered_trees[tree.get_instance_id()] = tree + + +func unregister_tree(tree: BeehaveTree) -> void: + _registered_trees.erase(tree.get_instance_id()) diff --git a/addons/beehave/debug/graph_edit.gd b/addons/beehave/debug/graph_edit.gd new file mode 100644 index 0000000..afdcd1e --- /dev/null +++ b/addons/beehave/debug/graph_edit.gd @@ -0,0 +1,259 @@ +@tool +extends GraphEdit + +const BeehaveGraphNode := preload("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 active_nodes: Array[String] +var progress: int = 0 +var layout_button: Button + + +func _ready() -> void: + custom_minimum_size = Vector2(100, 300) + 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_zoom_hbox().add_child(layout_button) + _update_layout_button() + + +func _update_graph() -> void: + if updating_graph: + return + + updating_graph = true + + clear_connections() + + for child in get_children(): + 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(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_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_children(): + child.set_meta("status", -1) + + +func process_tick(instance_id: int, status: int) -> void: + var node := get_node_or_null(str(instance_id)) + if node: + node.text = "Status: %s" % get_status(status) + node.set_status(status) + node.set_meta("status", status) + if status == 0 or status == 2: + if not active_nodes.has(node.name): + active_nodes.push_back(node.name) + + +func process_end(instance_id: int) -> void: + if not _is_same_tree(instance_id): + return + + for child in get_children(): + var status := child.get_meta("status", -1) + match status: + 0: + active_nodes.erase(child.name) + child.set_color(SUCCESS_COLOR) + 1: + active_nodes.erase(child.name) + child.set_color(INACTIVE_COLOR) + 2: + child.set_color(ACTIVE_COLOR) + _: + child.text = " " + child.set_status(status) + child.set_color(INACTIVE_COLOR) + + +func _is_same_tree(instance_id: int) -> bool: + return str(instance_id) == beehave_tree.get("id", "") + + +func _get_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: + if not c.from in active_nodes or not c.to in active_nodes: + continue + var from := get_node(String(c.from)) + var to := get_node(String(c.to)) + + if from.get_meta("status", -1) < 0 or to.get_meta("status", -1) < 0: + return + + var line := _get_connection_line(from.position + from.get_connection_output_position(c.from_port), to.position + to.get_connection_input_position(c.to_port)) + + 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/graph_node.gd b/addons/beehave/debug/graph_node.gd new file mode 100644 index 0000000..6b262af --- /dev/null +++ b/addons/beehave/debug/graph_node.gd @@ -0,0 +1,145 @@ +@tool +extends GraphNode + +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 = BeehaveUtils.get_frames() +var horizontal: bool = false + + +func _init(horizontal: bool = false) -> void: + 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) + add_theme_stylebox_override("selected_frame", frames.empty) + 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) + add_child(panel) + + var vbox_container := VBoxContainer.new() + panel.add_child(vbox_container) + + var title_size := 24 * BeehaveUtils.get_editor_scale() + var margin_container := MarginContainer.new() + margin_container.add_theme_constant_override("margin_top", -title_size - 2 * BeehaveUtils.get_editor_scale()) + margin_container.mouse_filter = Control.MOUSE_FILTER_PASS + vbox_container.add_child(margin_container) + + var title_container := HBoxContainer.new() + title_container.add_child(Control.new()) + title_container.mouse_filter = Control.MOUSE_FILTER_PASS + title_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + margin_container.add_child(title_container) + + icon_rect = TextureRect.new() + icon_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + title_container.add_child(icon_rect) + + title_label = Label.new() + title_label.add_theme_color_override("font_color", DEFAULT_COLOR) + title_label.add_theme_font_override("font", get_theme_font("title_font")) + title_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + title_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + title_label.text = title_text + title_container.add_child(title_label) + + title_container.add_child(Control.new()) + + container = VBoxContainer.new() + container.size_flags_vertical = Control.SIZE_EXPAND_FILL + container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + panel.add_child(container) + + label = Label.new() + label.text = " " if text.is_empty() else text + container.add_child(label) + + # For bottom port + add_child(Control.new()) + + minimum_size_changed.connect(_on_size_changed) + _on_size_changed.call_deferred() + + +func set_status(status: int) -> void: + panel.add_theme_stylebox_override("panel", _get_stylebox(status)) + + +func _get_stylebox(status: int) -> StyleBox: + match status: + 0: return frames.success + 1: return frames.failure + 2: return frames.running + _: return frames.normal + + +func set_slots(left_enabled: bool, right_enabled: bool) -> void: + if horizontal: + set_slot(1, left_enabled, 0, Color.WHITE, right_enabled, 0, Color.WHITE, PORT_LEFT_ICON, PORT_RIGHT_ICON) + else: + set_slot(0, left_enabled, 0, Color.WHITE, false, -2, Color.TRANSPARENT, PORT_TOP_ICON, null) + set_slot(2, false, -1, Color.TRANSPARENT, right_enabled, 0, Color.WHITE, null, PORT_BOTTOM_ICON) + + +func set_color(color: Color) -> void: + set_input_color(color) + set_output_color(color) + + +func set_input_color(color: Color) -> void: + set_slot_color_left(1 if horizontal else 0, color) + + +func set_output_color(color: Color) -> void: + set_slot_color_right(1 if horizontal else 2, color) + + +func _on_size_changed(): + add_theme_constant_override("port_offset", 12 * BeehaveUtils.get_editor_scale() if horizontal else round(size.x / 2.0)) diff --git a/addons/beehave/debug/icons/horizontal_layout.svg b/addons/beehave/debug/icons/horizontal_layout.svg new file mode 100644 index 0000000..235dc62 --- /dev/null +++ b/addons/beehave/debug/icons/horizontal_layout.svg @@ -0,0 +1 @@ + \ 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 new file mode 100644 index 0000000..539e518 --- /dev/null +++ b/addons/beehave/debug/icons/horizontal_layout.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bah77esichnyx" +path="res://.godot/imported/horizontal_layout.svg-d2a7af351e44f9bf61d0c938b6f47fac.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/horizontal_layout.svg" +dest_files=["res://.godot/imported/horizontal_layout.svg-d2a7af351e44f9bf61d0c938b6f47fac.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/icons/port_bottom.svg b/addons/beehave/debug/icons/port_bottom.svg new file mode 100644 index 0000000..3ce70e7 --- /dev/null +++ b/addons/beehave/debug/icons/port_bottom.svg @@ -0,0 +1 @@ + \ 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 new file mode 100644 index 0000000..8845c5b --- /dev/null +++ b/addons/beehave/debug/icons/port_bottom.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://da3b236rjbqns" +path="res://.godot/imported/port_bottom.svg-e5c5c61b642a79ab9c2b66ff56603d34.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/port_bottom.svg" +dest_files=["res://.godot/imported/port_bottom.svg-e5c5c61b642a79ab9c2b66ff56603d34.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/icons/port_left.svg b/addons/beehave/debug/icons/port_left.svg new file mode 100644 index 0000000..c1f6717 --- /dev/null +++ b/addons/beehave/debug/icons/port_left.svg @@ -0,0 +1 @@ + \ 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 new file mode 100644 index 0000000..7ea9827 --- /dev/null +++ b/addons/beehave/debug/icons/port_left.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnufc8p6spdtn" +path="res://.godot/imported/port_left.svg-69cd927c4db555f1edbb8d1f553ea2fd.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/port_left.svg" +dest_files=["res://.godot/imported/port_left.svg-69cd927c4db555f1edbb8d1f553ea2fd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/icons/port_right.svg b/addons/beehave/debug/icons/port_right.svg new file mode 100644 index 0000000..2560af5 --- /dev/null +++ b/addons/beehave/debug/icons/port_right.svg @@ -0,0 +1 @@ + \ 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 new file mode 100644 index 0000000..20931cd --- /dev/null +++ b/addons/beehave/debug/icons/port_right.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bbmd6vk23ympm" +path="res://.godot/imported/port_right.svg-f760bd8be2dd613d0d3848c998c92a2a.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/port_right.svg" +dest_files=["res://.godot/imported/port_right.svg-f760bd8be2dd613d0d3848c998c92a2a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/icons/port_top.svg b/addons/beehave/debug/icons/port_top.svg new file mode 100644 index 0000000..d3b99e1 --- /dev/null +++ b/addons/beehave/debug/icons/port_top.svg @@ -0,0 +1 @@ + \ 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 new file mode 100644 index 0000000..dec7820 --- /dev/null +++ b/addons/beehave/debug/icons/port_top.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bw8wmxdfom8eh" +path="res://.godot/imported/port_top.svg-d1b336cdc6a0dd570305782a1e56f61d.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/port_top.svg" +dest_files=["res://.godot/imported/port_top.svg-d1b336cdc6a0dd570305782a1e56f61d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/icons/vertical_layout.svg b/addons/beehave/debug/icons/vertical_layout.svg new file mode 100644 index 0000000..d59ffb0 --- /dev/null +++ b/addons/beehave/debug/icons/vertical_layout.svg @@ -0,0 +1 @@ + \ 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 new file mode 100644 index 0000000..8ddcfca --- /dev/null +++ b/addons/beehave/debug/icons/vertical_layout.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bpyxu6i1dx5qh" +path="res://.godot/imported/vertical_layout.svg-1a08fee4b09812a05bcf3defb8afcc4c.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/vertical_layout.svg" +dest_files=["res://.godot/imported/vertical_layout.svg-1a08fee4b09812a05bcf3defb8afcc4c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/tree_node.gd b/addons/beehave/debug/tree_node.gd new file mode 100644 index 0000000..6c104d8 --- /dev/null +++ b/addons/beehave/debug/tree_node.gd @@ -0,0 +1,255 @@ +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 + +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/icons/action.svg b/addons/beehave/icons/action.svg new file mode 100644 index 0000000..3916c89 --- /dev/null +++ b/addons/beehave/icons/action.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/action.svg.import b/addons/beehave/icons/action.svg.import new file mode 100644 index 0000000..cf8a612 --- /dev/null +++ b/addons/beehave/icons/action.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://btrq8e0kyxthg" +path="res://.godot/imported/action.svg-e8a91246d0ba9ba3cf84290d65648f06.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/action.svg" +dest_files=["res://.godot/imported/action.svg-e8a91246d0ba9ba3cf84290d65648f06.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/blackboard.svg b/addons/beehave/icons/blackboard.svg new file mode 100644 index 0000000..e4948a5 --- /dev/null +++ b/addons/beehave/icons/blackboard.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/blackboard.svg.import b/addons/beehave/icons/blackboard.svg.import new file mode 100644 index 0000000..4650058 --- /dev/null +++ b/addons/beehave/icons/blackboard.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dw7rom0hiff6c" +path="res://.godot/imported/blackboard.svg-18d4dfd4f6de558de250b67251ff1e69.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/blackboard.svg" +dest_files=["res://.godot/imported/blackboard.svg-18d4dfd4f6de558de250b67251ff1e69.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/category_bt.svg b/addons/beehave/icons/category_bt.svg new file mode 100644 index 0000000..8be61ae --- /dev/null +++ b/addons/beehave/icons/category_bt.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/category_bt.svg.import b/addons/beehave/icons/category_bt.svg.import new file mode 100644 index 0000000..c11e4f2 --- /dev/null +++ b/addons/beehave/icons/category_bt.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://qpdd6ue7x82h" +path="res://.godot/imported/category_bt.svg-8537bebd1c5f62dca3d7ee7f17efeed4.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/category_bt.svg" +dest_files=["res://.godot/imported/category_bt.svg-8537bebd1c5f62dca3d7ee7f17efeed4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/category_composite.svg b/addons/beehave/icons/category_composite.svg new file mode 100644 index 0000000..aa8b866 --- /dev/null +++ b/addons/beehave/icons/category_composite.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/category_composite.svg.import b/addons/beehave/icons/category_composite.svg.import new file mode 100644 index 0000000..0496273 --- /dev/null +++ b/addons/beehave/icons/category_composite.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://863s568sneja" +path="res://.godot/imported/category_composite.svg-43f66e63a7ccfa5ac8ec6da0583b3246.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/category_composite.svg" +dest_files=["res://.godot/imported/category_composite.svg-43f66e63a7ccfa5ac8ec6da0583b3246.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/category_decorator.svg b/addons/beehave/icons/category_decorator.svg new file mode 100644 index 0000000..165e3d6 --- /dev/null +++ b/addons/beehave/icons/category_decorator.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/category_decorator.svg.import b/addons/beehave/icons/category_decorator.svg.import new file mode 100644 index 0000000..492f32e --- /dev/null +++ b/addons/beehave/icons/category_decorator.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c2ie8m4ddawlb" +path="res://.godot/imported/category_decorator.svg-79d598d6456f32724156248e09d6eaf3.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/category_decorator.svg" +dest_files=["res://.godot/imported/category_decorator.svg-79d598d6456f32724156248e09d6eaf3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/category_leaf.svg b/addons/beehave/icons/category_leaf.svg new file mode 100644 index 0000000..1482fe6 --- /dev/null +++ b/addons/beehave/icons/category_leaf.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/category_leaf.svg.import b/addons/beehave/icons/category_leaf.svg.import new file mode 100644 index 0000000..4ef9604 --- /dev/null +++ b/addons/beehave/icons/category_leaf.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://eq0sp4g3s75r" +path="res://.godot/imported/category_leaf.svg-c740ecab6cfae632574ca5e39e46fd2e.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/category_leaf.svg" +dest_files=["res://.godot/imported/category_leaf.svg-c740ecab6cfae632574ca5e39e46fd2e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/condition.svg b/addons/beehave/icons/condition.svg new file mode 100644 index 0000000..37b2c7a --- /dev/null +++ b/addons/beehave/icons/condition.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/condition.svg.import b/addons/beehave/icons/condition.svg.import new file mode 100644 index 0000000..ef59099 --- /dev/null +++ b/addons/beehave/icons/condition.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ck4toqx0nggiu" +path="res://.godot/imported/condition.svg-57892684b10a64086f68c09c388b17e5.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/condition.svg" +dest_files=["res://.godot/imported/condition.svg-57892684b10a64086f68c09c388b17e5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/failer.svg b/addons/beehave/icons/failer.svg new file mode 100644 index 0000000..968f7e1 --- /dev/null +++ b/addons/beehave/icons/failer.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/failer.svg.import b/addons/beehave/icons/failer.svg.import new file mode 100644 index 0000000..989b556 --- /dev/null +++ b/addons/beehave/icons/failer.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2fj7htaqvcud" +path="res://.godot/imported/failer.svg-9a62b840e1eacc0437e7a67b14a302e4.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/failer.svg" +dest_files=["res://.godot/imported/failer.svg-9a62b840e1eacc0437e7a67b14a302e4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/inverter.svg b/addons/beehave/icons/inverter.svg new file mode 100644 index 0000000..d4e791e --- /dev/null +++ b/addons/beehave/icons/inverter.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/inverter.svg.import b/addons/beehave/icons/inverter.svg.import new file mode 100644 index 0000000..e9050a8 --- /dev/null +++ b/addons/beehave/icons/inverter.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cffmoc3og8hux" +path="res://.godot/imported/inverter.svg-1f1b976d95de42c4ad99a92fa9a6c5d0.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/inverter.svg" +dest_files=["res://.godot/imported/inverter.svg-1f1b976d95de42c4ad99a92fa9a6c5d0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/limiter.svg b/addons/beehave/icons/limiter.svg new file mode 100644 index 0000000..7b3fa1d --- /dev/null +++ b/addons/beehave/icons/limiter.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/limiter.svg.import b/addons/beehave/icons/limiter.svg.import new file mode 100644 index 0000000..7b56b08 --- /dev/null +++ b/addons/beehave/icons/limiter.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c7akxvsg0f2by" +path="res://.godot/imported/limiter.svg-b4c7646605c46f53c5e403fe21d8f584.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/limiter.svg" +dest_files=["res://.godot/imported/limiter.svg-b4c7646605c46f53c5e403fe21d8f584.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/selector.svg b/addons/beehave/icons/selector.svg new file mode 100644 index 0000000..0ae3b7a --- /dev/null +++ b/addons/beehave/icons/selector.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/selector.svg.import b/addons/beehave/icons/selector.svg.import new file mode 100644 index 0000000..ef7326d --- /dev/null +++ b/addons/beehave/icons/selector.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2c5d20doh4sp" +path="res://.godot/imported/selector.svg-78bccfc448bd1676b5a29bfde4b08e5b.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/selector.svg" +dest_files=["res://.godot/imported/selector.svg-78bccfc448bd1676b5a29bfde4b08e5b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/selector_random.svg b/addons/beehave/icons/selector_random.svg new file mode 100644 index 0000000..6f631e9 --- /dev/null +++ b/addons/beehave/icons/selector_random.svg @@ -0,0 +1,35 @@ + + diff --git a/addons/beehave/icons/selector_random.svg.import b/addons/beehave/icons/selector_random.svg.import new file mode 100644 index 0000000..6306f76 --- /dev/null +++ b/addons/beehave/icons/selector_random.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bmnkcmk7bkdjd" +path="res://.godot/imported/selector_random.svg-d52fea1352c24483ecd9dc8609cf00f3.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/selector_random.svg" +dest_files=["res://.godot/imported/selector_random.svg-d52fea1352c24483ecd9dc8609cf00f3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/selector_reactive.svg b/addons/beehave/icons/selector_reactive.svg new file mode 100644 index 0000000..6db005f --- /dev/null +++ b/addons/beehave/icons/selector_reactive.svg @@ -0,0 +1,45 @@ + + diff --git a/addons/beehave/icons/selector_reactive.svg.import b/addons/beehave/icons/selector_reactive.svg.import new file mode 100644 index 0000000..12a8c5b --- /dev/null +++ b/addons/beehave/icons/selector_reactive.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://crkbov0h8sb8l" +path="res://.godot/imported/selector_reactive.svg-dd3b8fb8cd2ffe331605aaad1e021cc0.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/selector_reactive.svg" +dest_files=["res://.godot/imported/selector_reactive.svg-dd3b8fb8cd2ffe331605aaad1e021cc0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/sequence.svg b/addons/beehave/icons/sequence.svg new file mode 100644 index 0000000..3ebedd9 --- /dev/null +++ b/addons/beehave/icons/sequence.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/sequence.svg.import b/addons/beehave/icons/sequence.svg.import new file mode 100644 index 0000000..5dadbe2 --- /dev/null +++ b/addons/beehave/icons/sequence.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5gw354thiofm" +path="res://.godot/imported/sequence.svg-76e5600611900cc81e9ec286977b8c6a.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/sequence.svg" +dest_files=["res://.godot/imported/sequence.svg-76e5600611900cc81e9ec286977b8c6a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/sequence_random.svg b/addons/beehave/icons/sequence_random.svg new file mode 100644 index 0000000..34e4a12 --- /dev/null +++ b/addons/beehave/icons/sequence_random.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/sequence_random.svg.import b/addons/beehave/icons/sequence_random.svg.import new file mode 100644 index 0000000..3907462 --- /dev/null +++ b/addons/beehave/icons/sequence_random.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bat8ptdw5qt1d" +path="res://.godot/imported/sequence_random.svg-58cee9098c622ef87db941279206422a.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/sequence_random.svg" +dest_files=["res://.godot/imported/sequence_random.svg-58cee9098c622ef87db941279206422a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/sequence_reactive.svg b/addons/beehave/icons/sequence_reactive.svg new file mode 100644 index 0000000..33d219b --- /dev/null +++ b/addons/beehave/icons/sequence_reactive.svg @@ -0,0 +1,60 @@ + + diff --git a/addons/beehave/icons/sequence_reactive.svg.import b/addons/beehave/icons/sequence_reactive.svg.import new file mode 100644 index 0000000..ab0fa25 --- /dev/null +++ b/addons/beehave/icons/sequence_reactive.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://rmiu1slwfkh7" +path="res://.godot/imported/sequence_reactive.svg-7d384ca290f7934adb9e17d9e7116b6c.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/sequence_reactive.svg" +dest_files=["res://.godot/imported/sequence_reactive.svg-7d384ca290f7934adb9e17d9e7116b6c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/succeeder.svg b/addons/beehave/icons/succeeder.svg new file mode 100644 index 0000000..10f5912 --- /dev/null +++ b/addons/beehave/icons/succeeder.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/succeeder.svg.import b/addons/beehave/icons/succeeder.svg.import new file mode 100644 index 0000000..0cb7334 --- /dev/null +++ b/addons/beehave/icons/succeeder.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dl6wo332kglbe" +path="res://.godot/imported/succeeder.svg-e5cf6f6e04b9b862b82fd2cb479272aa.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/succeeder.svg" +dest_files=["res://.godot/imported/succeeder.svg-e5cf6f6e04b9b862b82fd2cb479272aa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/tree.svg b/addons/beehave/icons/tree.svg new file mode 100644 index 0000000..6c85ea1 --- /dev/null +++ b/addons/beehave/icons/tree.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/tree.svg.import b/addons/beehave/icons/tree.svg.import new file mode 100644 index 0000000..9ac0308 --- /dev/null +++ b/addons/beehave/icons/tree.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://deryyg2hbmaaw" +path="res://.godot/imported/tree.svg-c0b20ed88b2fe300c0296f7236049076.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/tree.svg" +dest_files=["res://.godot/imported/tree.svg-c0b20ed88b2fe300c0296f7236049076.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/metrics/beehave_global_metrics.gd b/addons/beehave/metrics/beehave_global_metrics.gd new file mode 100644 index 0000000..9a2ba4a --- /dev/null +++ b/addons/beehave/metrics/beehave_global_metrics.gd @@ -0,0 +1,54 @@ +extends Node + +var _tree_count: int = 0 +var _active_tree_count: int = 0 +var _registered_trees: Array[BeehaveTree] = [] + + +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: BeehaveTree) -> 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: BeehaveTree) -> 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/nodes/beehave_node.gd b/addons/beehave/nodes/beehave_node.gd new file mode 100644 index 0000000..0c94b8c --- /dev/null +++ b/addons/beehave/nodes/beehave_node.gd @@ -0,0 +1,49 @@ +## A node in the behavior tree. Every node must return `SUCCESS`, `FAILURE` or +## `RUNNING` when ticked. +@tool +class_name BeehaveNode extends Node + +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_tree.gd b/addons/beehave/nodes/beehave_tree.gd new file mode 100644 index 0000000..7a8d145 --- /dev/null +++ b/addons/beehave/nodes/beehave_tree.gd @@ -0,0 +1,224 @@ +## Controls the flow of execution of the entire behavior tree. +@tool +@icon("../icons/tree.svg") +class_name BeehaveTree extends Node + +enum { + SUCCESS, + FAILURE, + RUNNING +} + +signal tree_enabled +signal tree_disabled + +## Wether this behavior tree should be enabled or not. +@export var enabled: bool = true: + set(value): + enabled = value + set_physics_process(enabled) + + if value: + tree_enabled.emit() + else: + interrupt() + tree_disabled.emit() + + get: + return enabled + +## An optional node path this behavior tree should apply to. +@export_node_path var actor_node_path : NodePath + +## 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: + 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) + BeehaveGlobalMetrics.register_tree(self) + else: + if _process_time_metric_name != '': + # Remove tree metric from the engine + Performance.remove_custom_monitor(_process_time_metric_name) + BeehaveGlobalMetrics.unregister_tree(self) + + BeehaveDebuggerMessages.unregister_tree(get_instance_id()) + +var actor : Node +var status : int = -1 + +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: + if Engine.is_editor_hint(): + return + + if self.get_child_count() > 0 and not self.get_child(0) is BeehaveNode: + push_warning("Beehave error: Root %s should have only one child of type BeehaveNode (NodePath: %s)" % [self.name, self.get_path()]) + disable() + return + + if not blackboard: + _internal_blackboard = Blackboard.new() + add_child(_internal_blackboard, false, Node.INTERNAL_MODE_BACK) + + actor = get_parent() + if actor_node_path: + actor = get_node(actor_node_path) + + # Get the name of the parent node name for metric + var parent_name = actor.name + _process_time_metric_name = "beehave [microseconds]/process_time_%s-%s" % [parent_name, get_instance_id()] + + # Register custom metric to the engine + if custom_monitor: + Performance.add_custom_monitor(_process_time_metric_name, _get_process_time_metric_value) + BeehaveGlobalMetrics.register_tree(self) + + set_physics_process(enabled) + BeehaveGlobalDebugger.register_tree(self) + BeehaveDebuggerMessages.register_tree(_get_debugger_data(self)) + + +func _physics_process(delta: float) -> void: + if Engine.is_editor_hint(): + return + + # Start timing for metric + var start_time = Time.get_ticks_usec() + + blackboard.set_value("can_send_message", _can_send_message) + + if _can_send_message: + BeehaveDebuggerMessages.process_begin(get_instance_id()) + + if self.get_child_count() == 1: + tick() + + if _can_send_message: + BeehaveDebuggerMessages.process_end(get_instance_id()) + + # Check the cost for this frame and save it for metric report + _process_time_metric_value = Time.get_ticks_usec() - start_time + + +func tick() -> int: + var child := self.get_child(0) + if status != RUNNING: + child.before_run(actor, blackboard) + + status = child.tick(actor, blackboard) + if _can_send_message: + BeehaveDebuggerMessages.process_tick(child.get_instance_id(), status) + BeehaveDebuggerMessages.process_tick(get_instance_id(), status) + + # Clear running action if nothing is running + if status != RUNNING: + blackboard.set_value("running_action", null, str(actor.get_instance_id())) + child.after_run(actor, blackboard) + + return status + + +func _get_configuration_warnings() -> PackedStringArray: + var warnings:PackedStringArray = [] + + if 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) + BeehaveGlobalMetrics.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 _process_time_metric_value + + +func _get_debugger_data(node: Node) -> Dictionary: + if not node is BeehaveTree and not 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"] diff --git a/addons/beehave/nodes/composites/composite.gd b/addons/beehave/nodes/composites/composite.gd new file mode 100644 index 0000000..b959eea --- /dev/null +++ b/addons/beehave/nodes/composites/composite.gd @@ -0,0 +1,40 @@ +## A Composite node controls the flow of execution of its children in a specific manner. +@tool +@icon("../../icons/category_composite.svg") +class_name Composite extends BeehaveNode + + +var running_child: BeehaveNode = null + + +func _ready(): + if Engine.is_editor_hint(): + return + + if self.get_child_count() < 1: + push_warning("BehaviorTree Error: Composite %s should have at least one child (NodePath: %s)" % [self.name, self.get_path()]) + + +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/randomized_composite.gd b/addons/beehave/nodes/composites/randomized_composite.gd new file mode 100644 index 0000000..0eb62c6 --- /dev/null +++ b/addons/beehave/nodes/composites/randomized_composite.gd @@ -0,0 +1,152 @@ +@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 + + +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 _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) + + +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) + + var children = get_children() + children.erase(node) + _update_weights(children) + + +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/selector.gd b/addons/beehave/nodes/composites/selector.gd new file mode 100644 index 0000000..eca71ac --- /dev/null +++ b/addons/beehave/nodes/composites/selector.gd @@ -0,0 +1,69 @@ +## 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. +@tool +@icon("../../icons/selector.svg") +class_name SelectorComposite extends Composite + + +var last_execution_index: int = 0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + for c in get_children(): + if c.get_index() < last_execution_index: + continue + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + _cleanup_running_task(c, actor, blackboard) + c.after_run(actor, blackboard) + return SUCCESS + FAILURE: + _cleanup_running_task(c, actor, blackboard) + last_execution_index += 1 + c.after_run(actor, blackboard) + RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + + return FAILURE + + +func after_run(actor: Node, blackboard: Blackboard) -> void: + last_execution_index = 0 + super(actor, blackboard) + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + last_execution_index = 0 + super(actor, blackboard) + + +## Changes `running_action` and `running_child` after the node finishes executing. +func _cleanup_running_task(finished_action: Node, actor: Node, blackboard: Blackboard): + var blackboard_name = str(actor.get_instance_id()) + if finished_action == running_child: + running_child = null + if finished_action == blackboard.get_value("running_action", null, blackboard_name): + blackboard.set_value("running_action", null, blackboard_name) + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SelectorComposite") + return classes diff --git a/addons/beehave/nodes/composites/selector_random.gd b/addons/beehave/nodes/composites/selector_random.gd new file mode 100644 index 0000000..b780b5f --- /dev/null +++ b/addons/beehave/nodes/composites/selector_random.gd @@ -0,0 +1,80 @@ +## This node will attempt to execute all of its children just like a +## [code]SelectorStar[/code] would, with the exception that the children +## will be executed in a random order. +@tool +@icon("../../icons/selector_random.svg") +class_name SelectorRandomComposite extends RandomizedComposite + +## A shuffled list of the children that will be executed in reverse order. +var _children_bag: Array[Node] = [] +var c: Node + +func _ready() -> void: + super() + if random_seed == 0: + randomize() + + +func tick(actor: Node, blackboard: Blackboard) -> int: + if _children_bag.is_empty(): + _reset() + + # We need to traverse the array in reverse since we will be manipulating it. + for i in _get_reversed_indexes(): + c = _children_bag[i] + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + _children_bag.erase(c) + c.after_run(actor, blackboard) + return SUCCESS + FAILURE: + _children_bag.erase(c) + c.after_run(actor, blackboard) + RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + + return FAILURE + + +func after_run(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + + +func _get_reversed_indexes() -> Array[int]: + var reversed: Array[int] + reversed.assign(range(_children_bag.size())) + reversed.reverse() + return reversed + + +func _reset() -> void: + var new_order = get_shuffled_children() + _children_bag = new_order.duplicate() + _children_bag.reverse() # It needs to run the children in reverse order. + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SelectorRandomComposite") + return classes diff --git a/addons/beehave/nodes/composites/selector_reactive.gd b/addons/beehave/nodes/composites/selector_reactive.gd new file mode 100644 index 0000000..869ef8a --- /dev/null +++ b/addons/beehave/nodes/composites/selector_reactive.gd @@ -0,0 +1,45 @@ +## 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. +@tool +@icon("../../icons/selector_reactive.svg") +class_name SelectorReactiveComposite extends Composite + +func tick(actor: Node, blackboard: Blackboard) -> int: + for c in get_children(): + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + # Interrupt any child that was RUNNING before. + if c != running_child: + interrupt(actor, blackboard) + c.after_run(actor, blackboard) + return SUCCESS + FAILURE: + c.after_run(actor, blackboard) + RUNNING: + if c != running_child: + interrupt(actor, blackboard) + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + + return FAILURE + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SelectorReactiveComposite") + return classes diff --git a/addons/beehave/nodes/composites/sequence.gd b/addons/beehave/nodes/composites/sequence.gd new file mode 100644 index 0000000..11b5d79 --- /dev/null +++ b/addons/beehave/nodes/composites/sequence.gd @@ -0,0 +1,76 @@ +## 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. +@tool +@icon("../../icons/sequence.svg") +class_name SequenceComposite extends Composite + + +var successful_index: int = 0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + for c in get_children(): + + if c.get_index() < successful_index: + continue + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + _cleanup_running_task(c, actor, blackboard) + successful_index += 1 + c.after_run(actor, blackboard) + FAILURE: + _cleanup_running_task(c, actor, blackboard) + # Interrupt any child that was RUNNING before. + interrupt(actor, blackboard) + c.after_run(actor, blackboard) + return FAILURE + RUNNING: + if c != running_child: + if running_child != null: + running_child.interrupt(actor, blackboard) + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + + _reset() + return SUCCESS + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + + +func _reset() -> void: + successful_index = 0 + + +## Changes `running_action` and `running_child` after the node finishes executing. +func _cleanup_running_task(finished_action: Node, actor: Node, blackboard: Blackboard): + var blackboard_name = str(actor.get_instance_id()) + if finished_action == running_child: + running_child = null + if finished_action == blackboard.get_value("running_action", null, blackboard_name): + blackboard.set_value("running_action", null, blackboard_name) + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SequenceComposite") + return classes diff --git a/addons/beehave/nodes/composites/sequence_random.gd b/addons/beehave/nodes/composites/sequence_random.gd new file mode 100644 index 0000000..ab8eb78 --- /dev/null +++ b/addons/beehave/nodes/composites/sequence_random.gd @@ -0,0 +1,92 @@ +## This node will attempt to execute all of its children just like a +## [code]SequenceStar[/code] would, with the exception that the children +## will be executed in a random order. +@tool +@icon("../../icons/sequence_random.svg") +class_name SequenceRandomComposite extends RandomizedComposite + +# Emitted whenever the children are shuffled. +signal reset(new_order: Array[Node]) + +## Whether the sequence should start where it left off after a previous failure. +@export var resume_on_failure: bool = false +## Whether the sequence should start where it left off after a previous interruption. +@export var resume_on_interrupt: bool = false + +## A shuffled list of the children that will be executed in reverse order. +var _children_bag: Array[Node] = [] +var c: Node + + +func _ready() -> void: + super() + if random_seed == 0: + randomize() + + +func tick(actor: Node, blackboard: Blackboard) -> int: + if _children_bag.is_empty(): + _reset() + + # We need to traverse the array in reverse since we will be manipulating it. + for i in _get_reversed_indexes(): + c = _children_bag[i] + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + _children_bag.erase(c) + c.after_run(actor, blackboard) + FAILURE: + _children_bag.erase(c) + c.after_run(actor, blackboard) + return FAILURE + RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + + return SUCCESS + + +func after_run(actor: Node, blackboard: Blackboard) -> void: + if not resume_on_failure: + _reset() + super(actor, blackboard) + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + if not resume_on_interrupt: + _reset() + super(actor, blackboard) + + +func _get_reversed_indexes() -> Array[int]: + var reversed: Array[int] + reversed.assign(range(_children_bag.size())) + reversed.reverse() + return reversed + + +func _reset() -> void: + var new_order = get_shuffled_children() + _children_bag = new_order.duplicate() + _children_bag.reverse() # It needs to run the children in reverse order. + reset.emit(new_order) + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SequenceRandomComposite") + return classes diff --git a/addons/beehave/nodes/composites/sequence_reactive.gd b/addons/beehave/nodes/composites/sequence_reactive.gd new file mode 100644 index 0000000..50e8b90 --- /dev/null +++ b/addons/beehave/nodes/composites/sequence_reactive.gd @@ -0,0 +1,62 @@ +## 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. +@tool +@icon("../../icons/sequence_reactive.svg") +class_name SequenceReactiveComposite extends Composite + + +var successful_index: int = 0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + for c in get_children(): + if c.get_index() < successful_index: + continue + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + successful_index += 1 + c.after_run(actor, blackboard) + FAILURE: + # Interrupt any child that was RUNNING before. + interrupt(actor, blackboard) + c.after_run(actor, blackboard) + return FAILURE + RUNNING: + _reset() + if running_child != c: + interrupt(actor, blackboard) + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + _reset() + return SUCCESS + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + +func _reset() -> void: + successful_index = 0 + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SequenceReactiveComposite") + return classes diff --git a/addons/beehave/nodes/composites/sequence_star.gd b/addons/beehave/nodes/composites/sequence_star.gd new file mode 100644 index 0000000..42ceb07 --- /dev/null +++ b/addons/beehave/nodes/composites/sequence_star.gd @@ -0,0 +1,58 @@ +## 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 restart. +@tool +@icon("../../icons/sequence_reactive.svg") +class_name SequenceStarComposite extends Composite + + +var successful_index: int = 0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + for c in get_children(): + if c.get_index() < successful_index: + continue + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + successful_index += 1 + c.after_run(actor, blackboard) + FAILURE: + 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/decorators/decorator.gd b/addons/beehave/nodes/decorators/decorator.gd new file mode 100644 index 0000000..8cc3944 --- /dev/null +++ b/addons/beehave/nodes/decorators/decorator.gd @@ -0,0 +1,41 @@ +## Decorator nodes are used to transform the result received by its child. +## Must only have one child. +@tool +@icon("../../icons/category_decorator.svg") +class_name Decorator extends BeehaveNode + + +var running_child: BeehaveNode = null + + +func _ready(): + if Engine.is_editor_hint(): + return + + if self.get_child_count() != 1: + push_warning("Beehave Error: Decorator %s should have only one child (NodePath: %s)" % [self.name, self.get_path()]) + + +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/failer.gd b/addons/beehave/nodes/decorators/failer.gd new file mode 100644 index 0000000..4a818ed --- /dev/null +++ b/addons/beehave/nodes/decorators/failer.gd @@ -0,0 +1,34 @@ +## A Failer node will always return a `FAILURE` status code. +@tool +@icon("../../icons/failer.svg") +class_name AlwaysFailDecorator extends Decorator + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var c = get_child(0) + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if response == RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + else: + c.after_run(actor, blackboard) + return FAILURE + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"AlwaysFailDecorator") + return classes diff --git a/addons/beehave/nodes/decorators/inverter.gd b/addons/beehave/nodes/decorators/inverter.gd new file mode 100644 index 0000000..16e3f36 --- /dev/null +++ b/addons/beehave/nodes/decorators/inverter.gd @@ -0,0 +1,42 @@ +## 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. +@tool +@icon("../../icons/inverter.svg") +class_name InverterDecorator extends Decorator + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var c = get_child(0) + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + c.after_run(actor, blackboard) + return FAILURE + FAILURE: + c.after_run(actor, blackboard) + return SUCCESS + RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + _: + push_error("This should be unreachable") + return -1 + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"InverterDecorator") + return classes diff --git a/addons/beehave/nodes/decorators/limiter.gd b/addons/beehave/nodes/decorators/limiter.gd new file mode 100644 index 0000000..72e7356 --- /dev/null +++ b/addons/beehave/nodes/decorators/limiter.gd @@ -0,0 +1,41 @@ +## The limiter will execute its child `x` amount of times. When the number of +## maximum ticks is reached, it will return a `FAILURE` status code. +@tool +@icon("../../icons/limiter.svg") +class_name LimiterDecorator extends Decorator + +@onready var cache_key = 'limiter_%s' % self.get_instance_id() + +@export var max_count : float = 0 + +func tick(actor: Node, blackboard: Blackboard) -> int: + var child = self.get_child(0) + var current_count = blackboard.get_value(cache_key, 0, str(actor.get_instance_id())) + + if current_count == 0: + child.before_run(actor, blackboard) + + if current_count < max_count: + blackboard.set_value(cache_key, current_count + 1, str(actor.get_instance_id())) + var response = child.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response) + + if child is ConditionLeaf: + blackboard.set_value("last_condition", child, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if child is ActionLeaf and response == RUNNING: + running_child = child + blackboard.set_value("running_action", child, str(actor.get_instance_id())) + + return response + else: + child.after_run(actor, blackboard) + return FAILURE + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"LimiterDecorator") + return classes diff --git a/addons/beehave/nodes/decorators/succeeder.gd b/addons/beehave/nodes/decorators/succeeder.gd new file mode 100644 index 0000000..9d9664b --- /dev/null +++ b/addons/beehave/nodes/decorators/succeeder.gd @@ -0,0 +1,34 @@ +## A succeeder node will always return a `SUCCESS` status code. +@tool +@icon("../../icons/succeeder.svg") +class_name AlwaysSucceedDecorator extends Decorator + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var c = get_child(0) + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if response == RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + else: + c.after_run(actor, blackboard) + return SUCCESS + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"AlwaysSucceedDecorator") + return classes diff --git a/addons/beehave/nodes/decorators/time_limiter.gd b/addons/beehave/nodes/decorators/time_limiter.gd new file mode 100644 index 0000000..cedb397 --- /dev/null +++ b/addons/beehave/nodes/decorators/time_limiter.gd @@ -0,0 +1,46 @@ +## The Time Limit Decorator will give its child a set amount of time to finish +## before interrupting it and return a `FAILURE` status code. The timer is reset +## every time before the node runs. +@tool +@icon("../../icons/limiter.svg") +class_name TimeLimiterDecorator extends Decorator + +@export var wait_time: = 0.0 + +var time_left: = 0.0 + +@onready var child: BeehaveNode = get_child(0) + + +func tick(actor: Node, blackboard: Blackboard) -> int: + if time_left < wait_time: + time_left += get_physics_process_delta_time() + var response = child.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response) + + if child is ConditionLeaf: + blackboard.set_value("last_condition", child, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if response == RUNNING: + running_child = child + if child is ActionLeaf: + blackboard.set_value("running_action", child, str(actor.get_instance_id())) + + return response + else: + child.after_run(actor, blackboard) + interrupt(actor, blackboard) + return FAILURE + + +func before_run(actor: Node, blackboard: Blackboard) -> void: + time_left = 0.0 + child.before_run(actor, blackboard) + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"TimeLimiterDecorator") + return classes diff --git a/addons/beehave/nodes/leaves/action.gd b/addons/beehave/nodes/leaves/action.gd new file mode 100644 index 0000000..003bdb4 --- /dev/null +++ b/addons/beehave/nodes/leaves/action.gd @@ -0,0 +1,13 @@ +## 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. +@tool +@icon("../../icons/action.svg") +class_name ActionLeaf extends Leaf + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"ActionLeaf") + return classes diff --git a/addons/beehave/nodes/leaves/blackboard_compare.gd b/addons/beehave/nodes/leaves/blackboard_compare.gd new file mode 100644 index 0000000..84fb43f --- /dev/null +++ b/addons/beehave/nodes/leaves/blackboard_compare.gd @@ -0,0 +1,61 @@ +## 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]. +@tool +class_name BlackboardCompareCondition extends ConditionLeaf + + +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_erase.gd b/addons/beehave/nodes/leaves/blackboard_erase.gd new file mode 100644 index 0000000..89508bd --- /dev/null +++ b/addons/beehave/nodes/leaves/blackboard_erase.gd @@ -0,0 +1,25 @@ +## Erases the specified key from the blackboard. +## Returns [code]FAILURE[/code] if expression execution fails, otherwise [code]SUCCESS[/code]. +@tool +class_name BlackboardEraseAction extends ActionLeaf + + +## 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_has.gd b/addons/beehave/nodes/leaves/blackboard_has.gd new file mode 100644 index 0000000..868fddf --- /dev/null +++ b/addons/beehave/nodes/leaves/blackboard_has.gd @@ -0,0 +1,23 @@ +## 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. +@tool +class_name BlackboardHasCondition extends ConditionLeaf + + +## 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_set.gd b/addons/beehave/nodes/leaves/blackboard_set.gd new file mode 100644 index 0000000..d0d6455 --- /dev/null +++ b/addons/beehave/nodes/leaves/blackboard_set.gd @@ -0,0 +1,34 @@ +## Sets the specified key to the specified value. +## Returns [code]FAILURE[/code] if expression execution fails, otherwise [code]SUCCESS[/code]. +@tool +class_name BlackboardSetAction extends ActionLeaf + + +## 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/condition.gd b/addons/beehave/nodes/leaves/condition.gd new file mode 100644 index 0000000..55ec6f9 --- /dev/null +++ b/addons/beehave/nodes/leaves/condition.gd @@ -0,0 +1,11 @@ +## Conditions are leaf nodes that either return SUCCESS or FAILURE depending on +## a single simple condition. They should never return `RUNNING`. +@tool +@icon("../../icons/condition.svg") +class_name ConditionLeaf extends Leaf + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"ConditionLeaf") + return classes diff --git a/addons/beehave/nodes/leaves/leaf.gd b/addons/beehave/nodes/leaves/leaf.gd new file mode 100644 index 0000000..b8c63b8 --- /dev/null +++ b/addons/beehave/nodes/leaves/leaf.gd @@ -0,0 +1,46 @@ +## Base class for all leaf nodes of the tree. +@tool +@icon("../../icons/category_leaf.svg") +class_name Leaf extends BeehaveNode + + +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/plugin.cfg b/addons/beehave/plugin.cfg new file mode 100644 index 0000000..8b61a2f --- /dev/null +++ b/addons/beehave/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Beehave" +description="🐝 Behavior Tree addon for Godot Engine" +author="bitbrain" +version="2.7.6" +script="plugin.gd" diff --git a/addons/beehave/plugin.gd b/addons/beehave/plugin.gd new file mode 100644 index 0000000..3c54627 --- /dev/null +++ b/addons/beehave/plugin.gd @@ -0,0 +1,24 @@ +@tool +extends EditorPlugin + +const BeehaveEditorDebugger := preload("debug/debugger.gd") +var editor_debugger: BeehaveEditorDebugger +var frames: RefCounted + +func _init(): + name = "BeehavePlugin" + add_autoload_singleton("BeehaveGlobalMetrics", "res://addons/beehave/metrics/beehave_global_metrics.gd") + add_autoload_singleton("BeehaveGlobalDebugger", "res://addons/beehave/debug/global_debugger.gd") + print("Beehave initialized!") + + +func _enter_tree() -> void: + editor_debugger = BeehaveEditorDebugger.new() + frames = preload("debug/frames.gd").new() + add_debugger_plugin(editor_debugger) + + +func _exit_tree() -> void: + remove_debugger_plugin(editor_debugger) + editor_debugger.free() + frames.free() diff --git a/addons/beehave/utils/utils.gd b/addons/beehave/utils/utils.gd new file mode 100644 index 0000000..32e3a03 --- /dev/null +++ b/addons/beehave/utils/utils.gd @@ -0,0 +1,22 @@ +@tool +class_name BeehaveUtils + + +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/resources_spreadsheet_view/main_screen/selection_actions.tscn b/addons/resources_spreadsheet_view/main_screen/selection_actions.tscn index 6206cbb..745980d 100644 --- a/addons/resources_spreadsheet_view/main_screen/selection_actions.tscn +++ b/addons/resources_spreadsheet_view/main_screen/selection_actions.tscn @@ -4,7 +4,7 @@ [ext_resource type="Script" path="res://addons/resources_spreadsheet_view/main_screen/selection_actions.gd" id="1_qv6ov"] [ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_color_setter.gd" id="2_a4ihj"] -[sub_resource type="Image" id="Image_rfojr"] +[sub_resource type="Image" id="Image_f27j1"] data = { "data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "LumAlpha8", @@ -14,7 +14,7 @@ data = { } [sub_resource type="ImageTexture" id="2"] -image = SubResource("Image_rfojr") +image = SubResource("Image_f27j1") [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ydxuk"] content_margin_left = 4.0 diff --git a/addons/resources_spreadsheet_view/saved_state.json b/addons/resources_spreadsheet_view/saved_state.json index cd0ffe9..2958890 100644 --- a/addons/resources_spreadsheet_view/saved_state.json +++ b/addons/resources_spreadsheet_view/saved_state.json @@ -8,6 +8,10 @@ "resource_local_to_scene": true, "resource_name": true }, + "res://config/character_move/": { + "resource_local_to_scene": true, + "resource_name": true + }, "res://config/player_skill/": { "animation_name": true, "has_animation": true, @@ -24,7 +28,8 @@ "res://example/Items/items/", "res://config/character/", "res://config/player_skill/", - "res://config/attack/" + "res://config/attack/", + "res://config/character_move/" ], "table_functions": { "filter": [ diff --git a/addons/resources_spreadsheet_view/typed_editors/dock_array.tscn b/addons/resources_spreadsheet_view/typed_editors/dock_array.tscn index afdada7..cff00e8 100644 --- a/addons/resources_spreadsheet_view/typed_editors/dock_array.tscn +++ b/addons/resources_spreadsheet_view/typed_editors/dock_array.tscn @@ -3,7 +3,7 @@ [ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_editors/dock_array.gd" id="1"] [ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_icon_button.gd" id="2"] -[sub_resource type="Image" id="Image_tduhc"] +[sub_resource type="Image" id="Image_lpd5n"] data = { "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -13,7 +13,7 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_3oshq"] -image = SubResource("Image_tduhc") +image = SubResource("Image_lpd5n") [node name="EditArray" type="VBoxContainer"] anchors_preset = 10 diff --git a/addons/resources_spreadsheet_view/typed_editors/dock_dict.tscn b/addons/resources_spreadsheet_view/typed_editors/dock_dict.tscn index 705e6d9..84f097a 100644 --- a/addons/resources_spreadsheet_view/typed_editors/dock_dict.tscn +++ b/addons/resources_spreadsheet_view/typed_editors/dock_dict.tscn @@ -3,7 +3,7 @@ [ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_editors/dock_dict.gd" id="1_2yivi"] [ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_icon_button.gd" id="2_yck0k"] -[sub_resource type="Image" id="Image_nywml"] +[sub_resource type="Image" id="Image_wuya0"] data = { "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -13,7 +13,7 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_3oshq"] -image = SubResource("Image_nywml") +image = SubResource("Image_wuya0") [node name="EditArray" type="VBoxContainer"] anchors_preset = 10 diff --git a/addons/resources_spreadsheet_view/typed_editors/dock_enum_array.tscn b/addons/resources_spreadsheet_view/typed_editors/dock_enum_array.tscn index 32b9979..f918fe0 100644 --- a/addons/resources_spreadsheet_view/typed_editors/dock_enum_array.tscn +++ b/addons/resources_spreadsheet_view/typed_editors/dock_enum_array.tscn @@ -3,7 +3,7 @@ [ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_editors/dock_enum_array.gd" id="1_n3flg"] [ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_icon_button.gd" id="2_mda1e"] -[sub_resource type="Image" id="Image_x8wr6"] +[sub_resource type="Image" id="Image_s5ag4"] data = { "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -13,7 +13,7 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_3oshq"] -image = SubResource("Image_x8wr6") +image = SubResource("Image_s5ag4") [node name="EditEnumArray" type="VBoxContainer"] anchors_preset = 10 diff --git a/config/character_move/normal.tres b/config/character_move/normal.tres index 225369e..db13f31 100644 --- a/config/character_move/normal.tres +++ b/config/character_move/normal.tres @@ -4,6 +4,6 @@ [resource] script = ExtResource("1_r4l81") -speed = 1.5 +speed = 3.0 gravity_scale = 1.0 -jump_velocity = 3.0 +jump_velocity = 4.0 diff --git a/config/player_skill/hero01_long_air_attack03.tres b/config/player_skill/hero01_long_air_attack03.tres index f18d677..05cbdf0 100644 --- a/config/player_skill/hero01_long_air_attack03.tres +++ b/config/player_skill/hero01_long_air_attack03.tres @@ -16,6 +16,6 @@ action = "attack_light" name = "" skill_animation = ExtResource("3_1erk8") attack_list = Array[Resource("res://script/config/attack_cfg.gd")]([ExtResource("1_x3v4o")]) -has_animation = false +refresh_animation = false sprite_frams = ExtResource("4_wrd60") animation_name = "long_air_attack03" diff --git a/config/player_skill/hero01_long_attack03.tres b/config/player_skill/hero01_long_attack03.tres index 73fdcdf..788312c 100644 --- a/config/player_skill/hero01_long_attack03.tres +++ b/config/player_skill/hero01_long_attack03.tres @@ -16,6 +16,6 @@ action = "attack_light" name = "" skill_animation = ExtResource("2_ugt3f") attack_list = Array[Resource("res://script/config/attack_cfg.gd")]([ExtResource("1_7ai5j")]) -has_animation = false +refresh_animation = false sprite_frams = ExtResource("3_sr2og") animation_name = "long_attack03" diff --git a/config/skill/hero01.tres b/config/skill/hero01.tres deleted file mode 100644 index 22f3ffe..0000000 --- a/config/skill/hero01.tres +++ /dev/null @@ -1,12 +0,0 @@ -[gd_resource type="Resource" script_class="SkillCfg" load_steps=3 format=3 uid="uid://p7rrgcllpisi"] - -[ext_resource type="Script" path="res://script/config/skill_cfg.gd" id="1_jpm54"] -[ext_resource type="SpriteFrames" uid="uid://2cb8lknel0ih" path="res://resource/animation/character/basic_move.aseprite" id="2_2pfl0"] - -[resource] -script = ExtResource("1_jpm54") -name = "" -attack_list = Array[Resource("res://script/config/attack_cfg.gd")]([]) -has_animation = false -sprite_frams = ExtResource("2_2pfl0") -animation_name = "" diff --git a/config/skill/monster01_attack01.tres b/config/skill/monster01_attack01.tres new file mode 100644 index 0000000..23a2739 --- /dev/null +++ b/config/skill/monster01_attack01.tres @@ -0,0 +1,14 @@ +[gd_resource type="Resource" script_class="SkillCfg" load_steps=4 format=3 uid="uid://bohydsbv7kxu3"] + +[ext_resource type="Script" path="res://script/config/skill_cfg.gd" id="1_r8y6y"] +[ext_resource type="Animation" uid="uid://b8ypa7uw0uam5" path="res://resource/skill_animation/monster01_attack01.tres" id="2_hmrt4"] +[ext_resource type="SpriteFrames" uid="uid://bs74u0yvluhky" path="res://resource/animation/character/monster01_attack.aseprite" id="3_4nfis"] + +[resource] +script = ExtResource("1_r8y6y") +name = "" +skill_animation = ExtResource("2_hmrt4") +attack_list = Array[Resource("res://script/config/attack_cfg.gd")]([]) +refresh_animation = false +sprite_frams = ExtResource("3_4nfis") +animation_name = "attack01" diff --git a/project.godot b/project.godot index 826b6ef..e7d2f07 100644 --- a/project.godot +++ b/project.godot @@ -22,7 +22,7 @@ resources_spreadsheet_view/context_menu_on_leftclick=false config/name="Touhou Gd" run/main_scene="res://scene/launcher.tscn" -config/features=PackedStringArray("4.2", "Forward Plus") +config/features=PackedStringArray("4.1", "Forward Plus") config/icon="res://icon.svg" [aseprite_importers] @@ -38,6 +38,8 @@ Setting="*res://script/_global/setting.gd" Enum="*res://script/_global/enum.gd" Util="*res://script/_global/util.gd" Global="*res://script/_global/global.gd" +BeehaveGlobalMetrics="*res://addons/beehave/metrics/beehave_global_metrics.gd" +BeehaveGlobalDebugger="*res://addons/beehave/debug/global_debugger.gd" [display] @@ -45,7 +47,7 @@ window/size/always_on_top=true [editor_plugins] -enabled=PackedStringArray("res://addons/MagicaVoxel_Importer_with_Extensions/plugin.cfg", "res://addons/nklbdev.aseprite_importers/plugin.cfg", "res://addons/resources_spreadsheet_view/plugin.cfg") +enabled=PackedStringArray("res://addons/MagicaVoxel_Importer_with_Extensions/plugin.cfg", "res://addons/beehave/plugin.cfg", "res://addons/nklbdev.aseprite_importers/plugin.cfg", "res://addons/resources_spreadsheet_view/plugin.cfg") [file_customization] diff --git a/render/mesh/slash1.obj.import b/render/mesh/slash1.obj.import index c2e9203..c822f98 100644 --- a/render/mesh/slash1.obj.import +++ b/render/mesh/slash1.obj.import @@ -19,4 +19,3 @@ generate_tangents=true scale_mesh=Vector3(1, 1, 1) offset_mesh=Vector3(0, 0, 0) optimize_mesh=true -force_disable_mesh_compression=false diff --git a/render/mesh/slash2.obj.import b/render/mesh/slash2.obj.import index fc865a1..8944f93 100644 --- a/render/mesh/slash2.obj.import +++ b/render/mesh/slash2.obj.import @@ -19,4 +19,3 @@ generate_tangents=true scale_mesh=Vector3(1, 1, 1) offset_mesh=Vector3(0, 0, 0) optimize_mesh=true -force_disable_mesh_compression=false diff --git a/render/process_material/slash_normal.tres b/render/process_material/slash_normal.tres index e95eb8b..3314587 100644 --- a/render/process_material/slash_normal.tres +++ b/render/process_material/slash_normal.tres @@ -9,14 +9,14 @@ gradient = SubResource("Gradient_fjosh") [resource] particle_flag_rotate_y = true -angle_min = 180.0 -angle_max = 180.0 direction = Vector3(0, 1, 0) spread = 0.0 +gravity = Vector3(0, 0, 0) angular_velocity_min = -720.0 angular_velocity_max = -720.0 -gravity = Vector3(0, 0, 0) tangential_accel_min = -100.0 -scale_min = 0.25 -scale_max = 0.25 +angle_min = 180.0 +angle_max = 180.0 +scale_min = 0.5 +scale_max = 0.5 color_ramp = SubResource("GradientTexture1D_cypke") diff --git a/render/texture/explodeDecal.png b/render/texture/shape/explodeDecal.png similarity index 100% rename from render/texture/explodeDecal.png rename to render/texture/shape/explodeDecal.png diff --git a/render/texture/explodeDecal.png.import b/render/texture/shape/explodeDecal.png.import similarity index 70% rename from render/texture/explodeDecal.png.import rename to render/texture/shape/explodeDecal.png.import index c252f30..da2918e 100644 --- a/render/texture/explodeDecal.png.import +++ b/render/texture/shape/explodeDecal.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://bjv7f83tdgq17" -path="res://.godot/imported/explodeDecal.png-f644d5db8c0778f842c076bf4d5a2eb8.ctex" +path="res://.godot/imported/explodeDecal.png-98bda2cf336166681eaff595a5bec12f.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://render/texture/explodeDecal.png" -dest_files=["res://.godot/imported/explodeDecal.png-f644d5db8c0778f842c076bf4d5a2eb8.ctex"] +source_file="res://render/texture/shape/explodeDecal.png" +dest_files=["res://.godot/imported/explodeDecal.png-98bda2cf336166681eaff595a5bec12f.ctex"] [params] diff --git a/render/texture/readiness_hero.png b/render/texture/shape/readiness_hero.png similarity index 100% rename from render/texture/readiness_hero.png rename to render/texture/shape/readiness_hero.png diff --git a/render/texture/readiness_hero.png.import b/render/texture/shape/readiness_hero.png.import similarity index 69% rename from render/texture/readiness_hero.png.import rename to render/texture/shape/readiness_hero.png.import index d8e9100..7bc6bd2 100644 --- a/render/texture/readiness_hero.png.import +++ b/render/texture/shape/readiness_hero.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://dpsxejelj58f8" -path="res://.godot/imported/readiness_hero.png-d9ad5917bf7df1f3d654dd39aaecd923.ctex" +path="res://.godot/imported/readiness_hero.png-79941e71d54e0f21f93bb8731b58c44c.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://render/texture/readiness_hero.png" -dest_files=["res://.godot/imported/readiness_hero.png-d9ad5917bf7df1f3d654dd39aaecd923.ctex"] +source_file="res://render/texture/shape/readiness_hero.png" +dest_files=["res://.godot/imported/readiness_hero.png-79941e71d54e0f21f93bb8731b58c44c.ctex"] [params] diff --git a/render/texture/readiness_monster.png b/render/texture/shape/readiness_monster.png similarity index 100% rename from render/texture/readiness_monster.png rename to render/texture/shape/readiness_monster.png diff --git a/render/texture/readiness_monster.png.import b/render/texture/shape/readiness_monster.png.import similarity index 69% rename from render/texture/readiness_monster.png.import rename to render/texture/shape/readiness_monster.png.import index 48b6124..a751647 100644 --- a/render/texture/readiness_monster.png.import +++ b/render/texture/shape/readiness_monster.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://xtipei54v35i" -path="res://.godot/imported/readiness_monster.png-c62163d25615aac211401f6ba719c65d.ctex" +path="res://.godot/imported/readiness_monster.png-7c27fc050064accb74fc75d7edb38639.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://render/texture/readiness_monster.png" -dest_files=["res://.godot/imported/readiness_monster.png-c62163d25615aac211401f6ba719c65d.ctex"] +source_file="res://render/texture/shape/readiness_monster.png" +dest_files=["res://.godot/imported/readiness_monster.png-7c27fc050064accb74fc75d7edb38639.ctex"] [params] diff --git a/render/texture/swordDecal.png b/render/texture/shape/swordDecal.png similarity index 100% rename from render/texture/swordDecal.png rename to render/texture/shape/swordDecal.png diff --git a/render/texture/swordDecal.png.import b/render/texture/shape/swordDecal.png.import similarity index 70% rename from render/texture/swordDecal.png.import rename to render/texture/shape/swordDecal.png.import index 5049424..ea35b7f 100644 --- a/render/texture/swordDecal.png.import +++ b/render/texture/shape/swordDecal.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://ceu7b4nuktlgy" -path="res://.godot/imported/swordDecal.png-cb01b48f8d07246ce447a89d9248f176.ctex" +path="res://.godot/imported/swordDecal.png-152ce59ae7b5e48a88a78d43374add4a.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://render/texture/swordDecal.png" -dest_files=["res://.godot/imported/swordDecal.png-cb01b48f8d07246ce447a89d9248f176.ctex"] +source_file="res://render/texture/shape/swordDecal.png" +dest_files=["res://.godot/imported/swordDecal.png-152ce59ae7b5e48a88a78d43374add4a.ctex"] [params] diff --git a/resource/skill_animation/hero01_long_attack01.tres b/resource/skill_animation/hero01_long_attack01.tres index 55f5955..d1ebb0e 100644 --- a/resource/skill_animation/hero01_long_attack01.tres +++ b/resource/skill_animation/hero01_long_attack01.tres @@ -75,7 +75,7 @@ tracks/5/keys = { "times": PackedFloat32Array(0.1, 0.3), "transitions": PackedFloat32Array(1, 1), "update": 1, -"values": [1.0, 0.0] +"values": [2.0, 0.0] } tracks/6/type = "method" tracks/6/imported = false diff --git a/resource/skill_animation/hero01_long_attack02.tres b/resource/skill_animation/hero01_long_attack02.tres index 4939220..969ed04 100644 --- a/resource/skill_animation/hero01_long_attack02.tres +++ b/resource/skill_animation/hero01_long_attack02.tres @@ -75,7 +75,7 @@ tracks/5/keys = { "times": PackedFloat32Array(0.1, 0.3), "transitions": PackedFloat32Array(1, 1), "update": 1, -"values": [1.0, 0.0] +"values": [2.0, 0.0] } tracks/6/type = "method" tracks/6/imported = false diff --git a/resource/skill_animation/hero01_long_attack03.tres b/resource/skill_animation/hero01_long_attack03.tres index a5d74ad..25adf07 100644 --- a/resource/skill_animation/hero01_long_attack03.tres +++ b/resource/skill_animation/hero01_long_attack03.tres @@ -75,7 +75,7 @@ tracks/5/keys = { "times": PackedFloat32Array(0.1, 0.3, 0.4, 0.5), "transitions": PackedFloat32Array(1, 1, 1, 1), "update": 1, -"values": [1.0, 0.0, 1.0, 0.0] +"values": [2.0, 0.0, 2.0, 0.0] } tracks/6/type = "method" tracks/6/imported = false diff --git a/resource/skill_animation/hero01_long_attack04.tres b/resource/skill_animation/hero01_long_attack04.tres index ecd71a3..4c68447 100644 --- a/resource/skill_animation/hero01_long_attack04.tres +++ b/resource/skill_animation/hero01_long_attack04.tres @@ -75,7 +75,7 @@ tracks/5/keys = { "times": PackedFloat32Array(0.1, 0.5, 0.6), "transitions": PackedFloat32Array(1, 1, 1), "update": 1, -"values": [0.5, 1.0, 0.0] +"values": [1.0, 3.0, 0.0] } tracks/6/type = "method" tracks/6/imported = false diff --git a/resource/skill_animation/hero01_long_flash.tres b/resource/skill_animation/hero01_long_flash.tres index f4a8118..50dc9ec 100644 --- a/resource/skill_animation/hero01_long_flash.tres +++ b/resource/skill_animation/hero01_long_flash.tres @@ -75,7 +75,7 @@ tracks/5/keys = { "times": PackedFloat32Array(0.1, 0.3), "transitions": PackedFloat32Array(1, 1), "update": 1, -"values": [4.0, 0.0] +"values": [6.0, 0.0] } tracks/6/type = "method" tracks/6/imported = false diff --git a/resource/skill_animation/hero01_long_skill01.tres b/resource/skill_animation/hero01_long_skill01.tres index 9c8a5b8..33d7e24 100644 --- a/resource/skill_animation/hero01_long_skill01.tres +++ b/resource/skill_animation/hero01_long_skill01.tres @@ -74,7 +74,7 @@ tracks/5/keys = { "times": PackedFloat32Array(0.1, 0.4), "transitions": PackedFloat32Array(1, 1), "update": 1, -"values": [4.0, 0.0] +"values": [6.0, 0.0] } tracks/6/type = "method" tracks/6/imported = false diff --git a/resource/skill_animation/monster01_attack01.tres b/resource/skill_animation/monster01_attack01.tres new file mode 100644 index 0000000..d31b4f5 --- /dev/null +++ b/resource/skill_animation/monster01_attack01.tres @@ -0,0 +1,43 @@ +[gd_resource type="Animation" load_steps=2 format=3 uid="uid://b8ypa7uw0uam5"] + +[ext_resource type="SpriteFrames" uid="uid://bs74u0yvluhky" path="res://resource/animation/character/monster01_attack.aseprite" id="1_7ykbn"] + +[resource] +resource_name = "monster01_attack01" +length = 0.8 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("View:sprite_frames") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [ExtResource("1_7ykbn")] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("View:animation") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": ["attack01"] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("View:frame") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [0, 1, 2, 3, 4, 5, 6, 7] +} diff --git a/resource/skill_animation_library/animation_library.tres b/resource/skill_animation_library/animation_library.tres index bad2329..a981371 100644 --- a/resource/skill_animation_library/animation_library.tres +++ b/resource/skill_animation_library/animation_library.tres @@ -1,4 +1,4 @@ -[gd_resource type="AnimationLibrary" load_steps=11 format=3 uid="uid://croik07a1qko5"] +[gd_resource type="AnimationLibrary" load_steps=12 format=3 uid="uid://croik07a1qko5"] [ext_resource type="Animation" uid="uid://p8l0puqxrkwh" path="res://resource/skill_animation/hero01_long_air_attack01.tres" id="1_b46g3"] [ext_resource type="Animation" uid="uid://daopmieibx3b7" path="res://resource/skill_animation/hero01_long_attack01.tres" id="1_nwjtl"] @@ -10,6 +10,7 @@ [ext_resource type="Animation" uid="uid://cwm116apu63n1" path="res://resource/skill_animation/hero01_long_flash.tres" id="5_fumom"] [ext_resource type="Animation" uid="uid://bjnkrte7660pt" path="res://resource/skill_animation/hero01_long_skill01.tres" id="5_kt0qw"] [ext_resource type="Animation" uid="uid://iprcbf277rf4" path="res://resource/skill_animation/hero01_long_skill02.tres" id="7_ui68j"] +[ext_resource type="Animation" uid="uid://b8ypa7uw0uam5" path="res://resource/skill_animation/monster01_attack01.tres" id="11_q5gn4"] [resource] _data = { @@ -22,5 +23,6 @@ _data = { "hero01_long_attack04": ExtResource("4_36e6x"), "hero01_long_flash": ExtResource("5_fumom"), "hero01_long_skill01": ExtResource("5_kt0qw"), -"hero01_long_skill02": ExtResource("7_ui68j") +"hero01_long_skill02": ExtResource("7_ui68j"), +"monster01_attack01": ExtResource("11_q5gn4") } diff --git a/scene/ai/action/find_target.tscn b/scene/ai/action/find_target.tscn new file mode 100644 index 0000000..fc10400 --- /dev/null +++ b/scene/ai/action/find_target.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://1t2pkg7j2dkg"] + +[ext_resource type="Script" path="res://script/ai/action/action_find_target.gd" id="1_jb1sy"] + +[node name="FindTarget" type="Node"] +script = ExtResource("1_jb1sy") diff --git a/scene/ai/action/move_to_target.tscn b/scene/ai/action/move_to_target.tscn new file mode 100644 index 0000000..ed61c03 --- /dev/null +++ b/scene/ai/action/move_to_target.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://06ww04hf5wl0"] + +[ext_resource type="Script" path="res://script/ai/action_with_target/action_move_to_target.gd" id="1_t8ni6"] + +[node name="MoveToTarget" type="Node"] +script = ExtResource("1_t8ni6") diff --git a/scene/character/character.tscn b/scene/character/character.tscn index 5b9d52d..00ff653 100644 --- a/scene/character/character.tscn +++ b/scene/character/character.tscn @@ -44,6 +44,7 @@ layers = 524288 material_override = SubResource("ShaderMaterial_3u7mw") lod_bias = 0.001 gi_mode = 0 +pixel_size = 0.02 double_sided = false alpha_cut = 2 texture_filter = 0 diff --git a/scene/character/monster.tscn b/scene/character/monster.tscn index e969d45..8d827f2 100644 --- a/scene/character/monster.tscn +++ b/scene/character/monster.tscn @@ -1,8 +1,29 @@ -[gd_scene load_steps=2 format=3 uid="uid://cdyymv2w1qr66"] +[gd_scene load_steps=7 format=3 uid="uid://c37rf5ecfrvwn"] [ext_resource type="PackedScene" uid="uid://ksxwg0alt2us" path="res://scene/character/character.tscn" id="1_eshlr"] +[ext_resource type="Script" path="res://script/character/monster/ai.gd" id="2_7ei2q"] +[ext_resource type="Script" path="res://addons/beehave/nodes/beehave_tree.gd" id="3_x3u4t"] +[ext_resource type="Script" path="res://addons/beehave/nodes/composites/sequence.gd" id="4_k2hsy"] +[ext_resource type="PackedScene" uid="uid://1t2pkg7j2dkg" path="res://scene/ai/action/find_target.tscn" id="5_t0771"] +[ext_resource type="PackedScene" uid="uid://06ww04hf5wl0" path="res://scene/ai/action/move_to_target.tscn" id="6_v4m1x"] [node name="Character" instance=ExtResource("1_eshlr")] +[node name="Status" parent="." index="1"] +speed_up_rate = -0.5 + [node name="View" parent="." index="2"] -animation = &"long_attack01" +animation = &"long_air_attack01" + +[node name="AI" type="Node3D" parent="." index="8"] +script = ExtResource("2_7ei2q") + +[node name="BeehaveTree" type="Node" parent="AI" index="0"] +script = ExtResource("3_x3u4t") + +[node name="SequenceComposite" type="Node" parent="AI/BeehaveTree" index="0"] +script = ExtResource("4_k2hsy") + +[node name="FindTarget" parent="AI/BeehaveTree/SequenceComposite" index="0" instance=ExtResource("5_t0771")] + +[node name="MoveToTarget" parent="AI/BeehaveTree/SequenceComposite" index="1" instance=ExtResource("6_v4m1x")] diff --git a/scene/effect/afterimage/normal.tscn b/scene/effect/afterimage/normal.tscn index 01f120e..a6abd8c 100644 --- a/scene/effect/afterimage/normal.tscn +++ b/scene/effect/afterimage/normal.tscn @@ -4,6 +4,7 @@ [ext_resource type="Script" path="res://script/effect/afterimage.gd" id="2_grtq1"] [node name="AfterImage" type="AnimatedSprite3D"] +pixel_size = 0.02 sprite_frames = ExtResource("1_1twno") animation = &"idle_loop" script = ExtResource("2_grtq1") diff --git a/scene/effect/decal/readiness_hero.tscn b/scene/effect/decal/readiness_hero.tscn index c01d8cc..38b381f 100644 --- a/scene/effect/decal/readiness_hero.tscn +++ b/scene/effect/decal/readiness_hero.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://7ej8d4b1lc0v"] -[ext_resource type="Texture2D" uid="uid://dpsxejelj58f8" path="res://render/texture/readiness_hero.png" id="1_gkbai"] +[ext_resource type="Texture2D" uid="uid://dpsxejelj58f8" path="res://render/texture/shape/readiness_hero.png" id="1_gkbai"] [node name="ReadinessHero" type="Decal"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.358925, 0) diff --git a/scene/effect/decal/readiness_monster.tscn b/scene/effect/decal/readiness_monster.tscn index eee86c7..c8213e1 100644 --- a/scene/effect/decal/readiness_monster.tscn +++ b/scene/effect/decal/readiness_monster.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://64e2u2uedpi1"] -[ext_resource type="Texture2D" uid="uid://xtipei54v35i" path="res://render/texture/readiness_monster.png" id="1_2mpei"] +[ext_resource type="Texture2D" uid="uid://xtipei54v35i" path="res://render/texture/shape/readiness_monster.png" id="1_2mpei"] [node name="ReadinessHero" type="Decal"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.358925, 0) diff --git a/scene/effect/particle/particle_slash_normal.tscn b/scene/effect/particle/particle_slash_normal.tscn index 1d7d8e1..f269094 100644 --- a/scene/effect/particle/particle_slash_normal.tscn +++ b/scene/effect/particle/particle_slash_normal.tscn @@ -10,7 +10,6 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.699187, 4.61636) script = ExtResource("1_fx8ev") [node name="Slash" type="GPUParticles3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.174235, 0, 0) material_override = ExtResource("1_6c80n") cast_shadow = 0 emitting = false diff --git a/scene/launcher.tscn b/scene/launcher.tscn index bab396d..5409a7a 100644 --- a/scene/launcher.tscn +++ b/scene/launcher.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=12 format=3 uid="uid://bkhnhc6bk3w7l"] +[gd_scene load_steps=11 format=3 uid="uid://eoydwrunmm5n"] [ext_resource type="Script" path="res://script/manager/game_manager.gd" id="1_q2t80"] [ext_resource type="ArrayMesh" uid="uid://cap7t5iwpjpi2" path="res://resource/level/levelground0000.vox" id="1_u51ir"] @@ -7,9 +7,8 @@ [ext_resource type="Script" path="res://script/editor_tool/editor_tool.gd" id="5_n3qhi"] [ext_resource type="PackedScene" uid="uid://cc525u8auypjf" path="res://scene/ui/profile_screen.tscn" id="6_u1fxn"] [ext_resource type="PackedScene" uid="uid://126wph4owvoy" path="res://scene/ui/hud_screen.tscn" id="7_gx646"] -[ext_resource type="Texture2D" uid="uid://bjv7f83tdgq17" path="res://render/texture/explodeDecal.png" id="7_hqv3v"] [ext_resource type="Script" path="res://script/manager/effect_manager.gd" id="8_0jv87"] -[ext_resource type="PackedScene" uid="uid://b2h4pcmlii7dg" path="res://scene/effect/particle/particle_slash_normal.tscn" id="10_gyy7x"] +[ext_resource type="Texture2D" uid="uid://bjv7f83tdgq17" path="res://render/texture/shape/explodeDecal.png" id="9_4x7bs"] [sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_2mxa4"] data = PackedVector3Array(-6.4, 0, -6.4, -3.2, 0, -6.4, -3.2, 0, 6.4, -3.2, 0, 6.4, -6.4, 0, 6.4, -6.4, 0, -6.4, -3.2, 0, 3.2, 6.4, 0, 3.2, 6.4, 0, 6.4, 6.4, 0, 6.4, -3.2, 0, 6.4, -3.2, 0, 3.2, -3.2, 0, -3.2, 3.2, 0, -3.2, 3.2, 0, 3.2, 3.2, 0, 3.2, -3.2, 0, 3.2, -3.2, 0, -3.2, -3.2, 0, -6.4, 6.4, 0, -6.4, 6.4, 0, -3.2, 6.4, 0, -3.2, -3.2, 0, -3.2, -3.2, 0, -6.4, 3.2, 0, -3.2, 6.4, 0, -3.2, 6.4, 0, 3.2, 6.4, 0, 3.2, 3.2, 0, 3.2, 3.2, 0, -3.2, -3.2, -0.1, 6.4, -3.2, -0.1, -6.4, -6.4, -0.1, -6.4, -6.4, -0.1, -6.4, -6.4, -0.1, 6.4, -3.2, -0.1, 6.4, 6.4, -0.1, 6.4, 6.4, -0.1, 3.2, -3.2, -0.1, 3.2, -3.2, -0.1, 3.2, -3.2, -0.1, 6.4, 6.4, -0.1, 6.4, 3.2, -0.1, 3.2, 3.2, -0.1, -3.2, -3.2, -0.1, -3.2, -3.2, -0.1, -3.2, -3.2, -0.1, 3.2, 3.2, -0.1, 3.2, 6.4, -0.1, -3.2, 6.4, -0.1, -6.4, -3.2, -0.1, -6.4, -3.2, -0.1, -6.4, -3.2, -0.1, -3.2, 6.4, -0.1, -3.2, 6.4, -0.1, 3.2, 6.4, -0.1, -3.2, 3.2, -0.1, -3.2, 3.2, -0.1, -3.2, 3.2, -0.1, 3.2, 6.4, -0.1, 3.2, -6.4, 0, -6.4, -6.4, 0, 6.4, -6.4, -0.1, 6.4, -6.4, -0.1, 6.4, -6.4, -0.1, -6.4, -6.4, 0, -6.4, 6.4, 0, -6.4, 6.4, -0.1, -6.4, 6.4, -0.1, 6.4, 6.4, -0.1, 6.4, 6.4, 0, 6.4, 6.4, 0, -6.4, -6.4, -0.1, 6.4, -6.4, 0, 6.4, 6.4, 0, 6.4, 6.4, 0, 6.4, 6.4, -0.1, 6.4, -6.4, -0.1, 6.4, 6.4, 0, -6.4, -6.4, 0, -6.4, -6.4, -0.1, -6.4, -6.4, -0.1, -6.4, 6.4, -0.1, -6.4, 6.4, 0, -6.4) @@ -23,7 +22,7 @@ script = ExtResource("1_q2t80") transform = Transform3D(1, 0, 0, 0, 0.707107, 0.707107, 0, -0.707107, 0.707107, 0, 4.01178, 2.85449) projection = 1 current = true -size = 5.0 +size = 8.0 frustum_offset = Vector2(2, 0) far = 20.0 script = ExtResource("4_yqiun") @@ -44,10 +43,6 @@ skeleton = NodePath("../../..") [node name="CollisionShape3D" type="CollisionShape3D" parent="GameManager/LevelManager/Levelground0000/StaticBody3D"] shape = SubResource("ConcavePolygonShape3D_2mxa4") -[node name="CSGBox3D" type="CSGBox3D" parent="GameManager/LevelManager"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.13326, 0, 0) -use_collision = true - [node name="CharacterManager" type="Node3D" parent="GameManager"] unique_name_in_owner = true script = ExtResource("4_oonkb") @@ -67,8 +62,6 @@ script = ExtResource("5_n3qhi") [node name="Decal" type="Decal" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.924324, -0.0136981, 0) size = Vector3(0.5, 0.5, 0.5) -texture_albedo = ExtResource("7_hqv3v") +texture_albedo = ExtResource("9_4x7bs") modulate = Color(0, 0, 0, 1) cull_mask = 1 - -[node name="Effect" parent="." instance=ExtResource("10_gyy7x")] diff --git a/script/_global/setting.gd b/script/_global/setting.gd index d831a37..169bbfd 100644 --- a/script/_global/setting.gd +++ b/script/_global/setting.gd @@ -1,7 +1,7 @@ extends Node3D #基本信息 -const pixel_size : float = 0.01 +const pixel_size : float = 0.02 const sprite_scale : float = 1.414 #技能系统 diff --git a/script/ai/action.gd b/script/ai/action.gd new file mode 100644 index 0000000..8517526 --- /dev/null +++ b/script/ai/action.gd @@ -0,0 +1,21 @@ +extends ActionLeaf +class_name Action + +func get_character(actor:Node) -> Character: + if not actor is AI: + print("行为树结构错误") + return null + var ai = actor as AI + return ai.character + +func before_run(actor:Node, blackboard: Blackboard) -> void: + init(get_character(actor)) + +func tick(actor:Node, blackboard: Blackboard) -> int: + var character = get_character(actor) + if character.get_status("is_stagger") or character.get_status("is_stun"): + return FAILURE + return run(character) + +func init(character: Character): pass +func run(character: Character) -> int: return FAILURE diff --git a/script/ai/action/action_find_target.gd b/script/ai/action/action_find_target.gd new file mode 100644 index 0000000..e184df3 --- /dev/null +++ b/script/ai/action/action_find_target.gd @@ -0,0 +1,6 @@ +extends Action + +func run(character: Character) -> int: + var player_id = Global.character_mgr.get_player_id() + character.set_status("target",player_id) + return SUCCESS diff --git a/script/ai/action_with_target.gd b/script/ai/action_with_target.gd new file mode 100644 index 0000000..a9e9d42 --- /dev/null +++ b/script/ai/action_with_target.gd @@ -0,0 +1,11 @@ +extends Action +class_name ActionWithTarget + +func init(character: Character): pass +func run(character: Character) -> int: + var target = Global.character_mgr.get_character(character.get_status("target")) + if not target: + return FAILURE + return execute(character,target) + +func execute(character: Character,target: Character) -> int : return FAILURE diff --git a/script/ai/action_with_target/action_move_to_target.gd b/script/ai/action_with_target/action_move_to_target.gd new file mode 100644 index 0000000..d46b5d3 --- /dev/null +++ b/script/ai/action_with_target/action_move_to_target.gd @@ -0,0 +1,12 @@ +extends ActionWithTarget + +func execute(character: Character, target: Character) -> int: + var dir = target.pos2D() - character.pos2D() + var dist = dir.length() + if dist < 1: + character.move_stop() + else: + print(dir) + character.move_to(dir) + return RUNNING + return SUCCESS diff --git a/script/character/battle.gd b/script/character/battle.gd index fd20157..374258f 100644 --- a/script/character/battle.gd +++ b/script/character/battle.gd @@ -28,7 +28,7 @@ func attack(): var pos_dir = enemy.pos2D()-character.pos2D() var distance = pos_dir.length() #test - if (distance < 1): + if (distance < 2): var hit_info = HitInfo.new() hit_info.from = character.id() hit_info.to = enemy.id() @@ -120,6 +120,9 @@ func settle(hit_info:HitInfo): #取消技能 if character_to.get_status("is_skill_running"): character_to.cancel_skill() + + #停止移动 + character_to.move_stop() #受击动画 var trigger_hit = "" diff --git a/script/character/character.gd b/script/character/character.gd index 08e4052..41ec3c3 100644 --- a/script/character/character.gd +++ b/script/character/character.gd @@ -50,6 +50,8 @@ func ui_pos()->Vector3:return position + status.ui_offset func get_status(status_name:String):return status.get_status(status_name) func set_status(status_name:String,value):status.set_status(status_name,value) func set_pos(pos:Vector3):position = pos +func move_to(dir:Vector2):set_status("move_dir",dir) +func move_stop():set_status("move_dir",Vector2.ZERO) func add_buff(buff_name:String,duration:float,ignore_pause:bool=false):buff.add_buff(buff_name,duration,ignore_pause) func remove_buff(buff_name:String):buff.remove_buff(buff_name) func has_buff(buff_name:String)->bool:return buff.has_buff(buff_name) diff --git a/script/character/effect.gd b/script/character/effect.gd index 6f055cf..01c35ad 100644 --- a/script/character/effect.gd +++ b/script/character/effect.gd @@ -12,6 +12,7 @@ class_name Effect var rediness : Decal var is_pause : bool +var is_right : bool func init(type:Enum.ECharacterType,body_scale:Vector3): match type: @@ -29,7 +30,11 @@ func _process(delta): var angle = status.move_dir.angle_to(Vector2.RIGHT) rediness.rotation.y = angle #flip - scale.x = 1 if status.is_right else -1 + if is_right != status.is_right: + is_right = status.is_right + for child in get_children(): + if child is Particle: + child.scale.x = 1 if is_right else -1 #pause if is_pause != status.is_pause: is_pause = status.is_pause @@ -62,6 +67,7 @@ func cast_attack_particle(): dir.x = abs(dir.x) var angle = dir.angle_to(Vector2.RIGHT) new_particle.rotation.y = angle + new_particle.scale.x = 1 if is_right else -1 add_child(new_particle) func release_effect(): diff --git a/script/character/monster/ai.gd b/script/character/monster/ai.gd new file mode 100644 index 0000000..74aa964 --- /dev/null +++ b/script/character/monster/ai.gd @@ -0,0 +1,8 @@ +extends Node3D +class_name AI + +@onready var character = (get_owner() as Character) +@onready var status = (%Status as Status) + +func _ready(): + pass diff --git a/script/character/move.gd b/script/character/move.gd index 9ddbbe1..df36403 100644 --- a/script/character/move.gd +++ b/script/character/move.gd @@ -32,7 +32,7 @@ func update_speed_y(delta): status.speed_y = character.velocity.y func update_move(delta): - var move_velocity = status.move_dir * (status.cfg.move.speed * (1 + status.speed_up_rate)) + var move_velocity = status.move_dir.normalized() * (status.cfg.move.speed * (1 + status.speed_up_rate)) var skill_velocity = status.skill_dir * status.skill_move_speed var hit_back_velocity = status.hit_back_dir * status.hit_back_speed move_velocity += skill_velocity + hit_back_velocity diff --git a/script/character/status.gd b/script/character/status.gd index 5591ea8..77728c9 100644 --- a/script/character/status.gd +++ b/script/character/status.gd @@ -17,6 +17,7 @@ class_name Status @export var stun : float #当前眩晕值 @export var stun_max : float #眩晕值最大值 @export var is_dead : bool #是否死亡 +@export var target : int #目标角色 @export_category("表现状态") @export var basic_offset : Vector3 #基本偏移值 diff --git a/script/character/view.gd b/script/character/view.gd index 33323dc..e965e06 100644 --- a/script/character/view.gd +++ b/script/character/view.gd @@ -148,12 +148,16 @@ func _on_animation_finished(): update_trans(true) func update_material(): - var tex = sprite_frames.get_frame_texture(animation,frame) var material = material_override as ShaderMaterial - material.set_shader_parameter("tex",tex) material.set_shader_parameter("flash_white",status.flash_white_rate) material.set_shader_parameter("deformation_dir",status.deformation_dir) material.set_shader_parameter("deformation_rate",status.deformation_rate*0.4) + call_deferred("refresh_texture") + +func refresh_texture(): + var material = material_override as ShaderMaterial + var tex = sprite_frames.get_frame_texture(animation,frame) + material.set_shader_parameter("tex",tex) func clone(target:AnimatedSprite3D): target.sprite_frames = sprite_frames diff --git a/script/manager/character_manager.gd b/script/manager/character_manager.gd index dabf5cd..22e6d28 100644 --- a/script/manager/character_manager.gd +++ b/script/manager/character_manager.gd @@ -3,6 +3,7 @@ class_name CharacterManager var character_map = {} var character_idx : int = 0 +var player_id : int func _ready(): Global.character_mgr = self @@ -24,6 +25,9 @@ func create_character(cfg:CharacterCfg,team:Enum.ETeam,pos:Vector3): character.set_pos(pos) SignalManager.character_create.emit(character_idx,team,pos) character.init_after() + + if cfg.type == Enum.ECharacterType.Player: + player_id = character_idx func destroy_character(id:int): if not id in character_map: @@ -38,6 +42,9 @@ func get_character(id:int) -> Character: else: return null +func get_player() -> Character:return get_character(player_id) +func get_player_id() -> int:return player_id + func get_enemy_list(id:int) -> Array[Character]: var ret:Array[Character] = [] var target = get_character(id)