Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix 4.2 Debugger Visuals #345

Merged
merged 3 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions addons/beehave/debug/debugger_tab.gd
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd")

signal make_floating

const BeehaveGraphEdit := preload("graph_edit.gd")
const OldBeehaveGraphEdit := preload("old_graph_edit.gd")
const NewBeehaveGraphEdit := preload("new_graph_edit.gd")

const TREE_ICON := preload("../icons/tree.svg")

var graph
var container: HSplitContainer
var item_list: ItemList
var graph: BeehaveGraphEdit
var message: Label

var active_trees: Dictionary
Expand All @@ -26,8 +28,11 @@ func _ready() -> void:
item_list.custom_minimum_size = Vector2(200, 0)
item_list.item_selected.connect(_on_item_selected)
container.add_child(item_list)
if Engine.get_version_info().minor >= 2:
graph = NewBeehaveGraphEdit.new(BeehaveUtils.get_frames())
else:
graph = OldBeehaveGraphEdit.new(BeehaveUtils.get_frames())

graph = BeehaveGraphEdit.new(BeehaveUtils.get_frames())
container.add_child(graph)

message = Label.new()
Expand Down
69 changes: 69 additions & 0 deletions addons/beehave/debug/new_frames.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
@tool
extends RefCounted


const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd")


const SUCCESS_COLOR := Color("#07783a")
const NORMAL_COLOR := Color("#15181e")
const FAILURE_COLOR := Color("#82010b")
const RUNNING_COLOR := Color("#c29c06")

var panel_normal: StyleBoxFlat
var panel_success: StyleBoxFlat
var panel_failure: StyleBoxFlat
var panel_running: StyleBoxFlat

var titlebar_normal: StyleBoxFlat
var titlebar_success: StyleBoxFlat
var titlebar_failure: StyleBoxFlat
var titlebar_running: StyleBoxFlat


func _init() -> void:
var plugin := BeehaveUtils.get_plugin()
if not plugin:
return


titlebar_normal = (
plugin
.get_editor_interface()
.get_base_control()
.get_theme_stylebox(&"titlebar", &"GraphNode")\
.duplicate()
)
titlebar_success = titlebar_normal.duplicate()
titlebar_failure = titlebar_normal.duplicate()
titlebar_running = titlebar_normal.duplicate()

titlebar_success.bg_color = SUCCESS_COLOR
titlebar_failure.bg_color = FAILURE_COLOR
titlebar_running.bg_color = RUNNING_COLOR

titlebar_success.border_color = SUCCESS_COLOR
titlebar_failure.border_color = FAILURE_COLOR
titlebar_running.border_color = RUNNING_COLOR


panel_normal = (
plugin
.get_editor_interface()
.get_base_control()
.get_theme_stylebox(&"panel", &"GraphNode")
.duplicate()
)
panel_success = (
plugin
.get_editor_interface()
.get_base_control()
.get_theme_stylebox(&"panel_selected", &"GraphNode")
.duplicate()
)
panel_failure = panel_success.duplicate()
panel_running = panel_success.duplicate()

panel_success.border_color = SUCCESS_COLOR
panel_failure.border_color = FAILURE_COLOR
panel_running.border_color = RUNNING_COLOR
296 changes: 296 additions & 0 deletions addons/beehave/debug/new_graph_edit.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
@tool
extends GraphEdit

const BeehaveGraphNode := preload("new_graph_node.gd")

const HORIZONTAL_LAYOUT_ICON := preload("icons/horizontal_layout.svg")
const VERTICAL_LAYOUT_ICON := preload("icons/vertical_layout.svg")

const PROGRESS_SHIFT: int = 50
const INACTIVE_COLOR: Color = Color("#898989")
const ACTIVE_COLOR: Color = Color("#c29c06")
const SUCCESS_COLOR: Color = Color("#07783a")


var updating_graph: bool = false
var arraging_nodes: bool = false
var beehave_tree: Dictionary:
set(value):
if beehave_tree == value:
return
beehave_tree = value
active_nodes.clear()
_update_graph()

var horizontal_layout: bool = false:
set(value):
if updating_graph or arraging_nodes:
return
if horizontal_layout == value:
return
horizontal_layout = value
_update_layout_button()
_update_graph()


var frames:RefCounted
var active_nodes: Array[String]
var progress: int = 0
var layout_button: Button


func _init(frames:RefCounted) -> void:
self.frames = frames


func _ready() -> void:
custom_minimum_size = Vector2(100, 300)
set("show_arrange_button", true)
minimap_enabled = false
layout_button = Button.new()
layout_button.flat = true
layout_button.focus_mode = Control.FOCUS_NONE
layout_button.pressed.connect(func(): horizontal_layout = not horizontal_layout)
get_menu_container().add_child(layout_button)
_update_layout_button()


func _update_graph() -> void:
if updating_graph:
return

updating_graph = true

clear_connections()

for child in _get_child_nodes():
remove_child(child)
child.queue_free()

if not beehave_tree.is_empty():
_add_nodes(beehave_tree)
_connect_nodes(beehave_tree)
_arrange_nodes.call_deferred(beehave_tree)

updating_graph = false


func _add_nodes(node: Dictionary) -> void:
if node.is_empty():
return
var gnode := BeehaveGraphNode.new(frames, horizontal_layout)
add_child(gnode)
gnode.title_text = node.name
gnode.name = node.id
gnode.icon = _get_icon(node.type.back())

if node.type.has(&"BeehaveTree"):
gnode.set_slots(false, true)
elif node.type.has(&"Leaf"):
gnode.set_slots(true, false)
elif node.type.has(&"Composite") or node.type.has(&"Decorator"):
gnode.set_slots(true, true)

for child in node.get("children", []):
_add_nodes(child)


func _connect_nodes(node: Dictionary) -> void:
for child in node.get("children", []):
connect_node(node.id, 0, child.id, 0)
_connect_nodes(child)


func _arrange_nodes(node: Dictionary) -> void:
if arraging_nodes:
return

arraging_nodes = true

var tree_node := _create_tree_nodes(node)
tree_node.update_positions(horizontal_layout)
_place_nodes(tree_node)

arraging_nodes = false


func _create_tree_nodes(node: Dictionary, root: TreeNode = null) -> TreeNode:
var tree_node := TreeNode.new(get_node(node.id), root)
for child in node.get("children", []):
var child_node := _create_tree_nodes(child, tree_node)
tree_node.children.push_back(child_node)
return tree_node


func _place_nodes(node: TreeNode) -> void:
node.item.position_offset = Vector2(node.x, node.y)
for child in node.children:
_place_nodes(child)


func _get_icon(type: StringName) -> Texture2D:
var classes := ProjectSettings.get_global_class_list()
for c in classes:
if c["class"] == type:
var icon_path := c.get("icon", String())
if not icon_path.is_empty():
return load(icon_path)
return null


func get_menu_container() -> Control:
return call("get_menu_hbox")


func get_status(status: int) -> String:
if status == 0:
return "SUCCESS"
elif status == 1:
return "FAILURE"
return "RUNNING"


func process_begin(instance_id: int) -> void:
if not _is_same_tree(instance_id):
return

for child in _get_child_nodes():
child.set_meta("status", -1)


func process_tick(instance_id: int, status: int) -> void:
var node := get_node_or_null(str(instance_id))
if node:
node.text = "Status: %s" % get_status(status)
node.set_status(status)
node.set_meta("status", status)
if status == 0 or status == 2:
if not active_nodes.has(node.name):
active_nodes.push_back(node.name)


func process_end(instance_id: int) -> void:
if not _is_same_tree(instance_id):
return

for child in _get_child_nodes():
var status := child.get_meta("status", -1)
match status:
0:
active_nodes.erase(child.name)
child.set_color(SUCCESS_COLOR)
1:
active_nodes.erase(child.name)
child.set_color(INACTIVE_COLOR)
2:
child.set_color(ACTIVE_COLOR)
_:
child.text = " "
child.set_status(status)
child.set_color(INACTIVE_COLOR)


func _is_same_tree(instance_id: int) -> bool:
return str(instance_id) == beehave_tree.get("id", "")


func _get_child_nodes() -> Array[Node]:
return get_children().filter(func(child): return child is BeehaveGraphNode)


func _get_connection_line(from_position: Vector2, to_position: Vector2) -> PackedVector2Array:
for child in _get_child_nodes():
for port in child.get_input_port_count():
if not (child.position_offset + child.get_input_port_position(port)).is_equal_approx(to_position):
continue
to_position = child.position_offset + child.get_custom_input_port_position(horizontal_layout)
for port in child.get_output_port_count():
if not (child.position_offset + child.get_output_port_position(port)).is_equal_approx(from_position):
continue
from_position = child.position_offset + child.get_custom_output_port_position(horizontal_layout)
return _get_elbow_connection_line(from_position, to_position)


func _get_elbow_connection_line(from_position: Vector2, to_position: Vector2) -> PackedVector2Array:
var points: PackedVector2Array

points.push_back(from_position)

var mid_position := ((to_position + from_position) / 2).round()
if horizontal_layout:
points.push_back(Vector2(mid_position.x, from_position.y))
points.push_back(Vector2(mid_position.x, to_position.y))
else:
points.push_back(Vector2(from_position.x, mid_position.y))
points.push_back(Vector2(to_position.x, mid_position.y))

points.push_back(to_position)

return points


func _process(delta: float) -> void:
if not active_nodes.is_empty():
progress += 10 if delta >= 0.05 else 1
if progress >= 1000:
progress = 0
queue_redraw()


func _draw() -> void:
if active_nodes.is_empty():
return

var circle_size: float = max(3, 6 * zoom)
var progress_shift: float = PROGRESS_SHIFT * zoom

var connections := get_connection_list()
for c in connections:
var from_node: StringName
var to_node: StringName

from_node = c.from_node
to_node = c.to_node

if not from_node in active_nodes or not c.to_node in active_nodes:
continue

var from := get_node(String(from_node))
var to := get_node(String(to_node))

if from.get_meta("status", -1) < 0 or to.get_meta("status", -1) < 0:
return

var output_port_position: Vector2
var input_port_position: Vector2

var scale_factor: float = from.get_rect().size.x / from.size.x

var line := _get_elbow_connection_line(
from.position + from.get_custom_output_port_position(horizontal_layout) * scale_factor,
to.position + to.get_custom_input_port_position(horizontal_layout) * scale_factor
)

var curve = Curve2D.new()
for l in line:
curve.add_point(l)

var max_steps := int(curve.get_baked_length())
var current_shift := progress % max_steps
var p := curve.sample_baked(current_shift)
draw_circle(p, circle_size, ACTIVE_COLOR)

var shift := current_shift - progress_shift
while shift >= 0:
draw_circle(curve.sample_baked(shift), circle_size, ACTIVE_COLOR)
shift -= progress_shift

shift = current_shift + progress_shift
while shift <= curve.get_baked_length():
draw_circle(curve.sample_baked(shift), circle_size, ACTIVE_COLOR)
shift += progress_shift


func _update_layout_button() -> void:
layout_button.icon = VERTICAL_LAYOUT_ICON if horizontal_layout else HORIZONTAL_LAYOUT_ICON
layout_button.tooltip_text = "Switch to Vertical layout" if horizontal_layout else "Switch to Horizontal layout"
Loading
Loading