Skip to content

Commit

Permalink
Fix 4.2 Debugger Visuals (#345)
Browse files Browse the repository at this point in the history
* fix debugger visuals

* additional cleanup

* check font and queue_free titlebar child

fixes failed debugger_test.gd case and orphans
  • Loading branch information
Snaiel authored Jun 26, 2024
1 parent cf04ed3 commit 42984eb
Show file tree
Hide file tree
Showing 8 changed files with 541 additions and 36 deletions.
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

0 comments on commit 42984eb

Please sign in to comment.