-
-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix debugger visuals * additional cleanup * check font and queue_free titlebar child fixes failed debugger_test.gd case and orphans
- Loading branch information
Showing
8 changed files
with
541 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
Oops, something went wrong.