122 lines
4.7 KiB
GDScript
122 lines
4.7 KiB
GDScript
extends Window
|
|
|
|
const D_TREE_NODE: PackedScene = preload("res://scenes/state/visualization/d_tree_node.tscn")
|
|
@onready var graph_edit: GraphEdit = %GraphEdit
|
|
|
|
# input data
|
|
var state_machine: StateMachine
|
|
# nodes
|
|
var all_nodes: Array[DTreeNode] = []
|
|
var all_node_names_to_nodes: Dictionary = {}
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
if Input.is_action_just_pressed("ui_accept"):
|
|
if is_visible():
|
|
hide()
|
|
else:
|
|
show()
|
|
|
|
# highlight the current state
|
|
for node in all_nodes:
|
|
node.set_highlighted(false)
|
|
|
|
if state_machine and state_machine.current_state:
|
|
var current_state_name: StringName = state_machine.current_state.get_name()
|
|
if all_node_names_to_nodes.has(current_state_name):
|
|
all_node_names_to_nodes[current_state_name].set_highlighted(true)
|
|
|
|
|
|
func build_tree():
|
|
if not state_machine:
|
|
push_error("No state machine set.")
|
|
return
|
|
|
|
var node_positions: Array[DTreeNode] = []
|
|
var y_spacing: int = 300
|
|
var x_spacing: int = 600
|
|
var depths: Dictionary = calculate_node_depths()
|
|
|
|
var states = state_machine.state_machine_data["states"]
|
|
|
|
# Place nodes
|
|
for i in range(states.size()):
|
|
var state_name = states.keys()[i]
|
|
var state_data = states[state_name]
|
|
|
|
var graph_node: DTreeNode = D_TREE_NODE.instantiate()
|
|
graph_edit.add_child(graph_node)
|
|
graph_node.title = state_name
|
|
|
|
all_nodes.append(graph_node)
|
|
all_node_names_to_nodes[state_name] = graph_node
|
|
|
|
# Connect nodes
|
|
for i in range(states.size()):
|
|
var state_name = states.keys()[i]
|
|
var state_data = states[state_name]
|
|
var graph_node: DTreeNode = all_nodes[i]
|
|
|
|
# Set up labels (input and output)
|
|
populate_node_labels(graph_node, state_name, state_data, all_node_names_to_nodes)
|
|
|
|
# Calculate position based on tree depth
|
|
var depth: int = depths[i]
|
|
var siblings: Array = get_nodes_at_depth(depth, depths)
|
|
@warning_ignore("integer_division")
|
|
var y_pos: int = y_spacing * (siblings.find(i) - (siblings.size() - 1) / 2)
|
|
var x_pos: int = x_spacing * depth
|
|
y_pos += randi() % 200 - 100
|
|
graph_node.position_offset = Vector2(x_pos, y_pos)
|
|
node_positions.append(graph_node)
|
|
|
|
|
|
func _on_graph_edit_connection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void:
|
|
print("Connection request from %s [%d] to %s [%d]" % [from_node, from_port, to_node, to_port])
|
|
|
|
|
|
# using connect_node(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> int:
|
|
func populate_node_labels(node: DTreeNode, state_name: String, state_data: Dictionary, node_names_to_nodes: Dictionary) -> void:
|
|
# start from the node passed in and only use the output connections to determine the input connections on the other nodes
|
|
var output_transitions: Array = state_data.get("transitions", [])
|
|
for transition in output_transitions:
|
|
var target_state_name: String = transition['target']
|
|
var port_out: Vector3i = node.add_label(state_machine.human_readable_transition(transition), false, true)
|
|
|
|
var target_node: DTreeNode = node_names_to_nodes[target_state_name]
|
|
if target_node:
|
|
var port_in: Vector3i = target_node.add_label("%s" % [state_name], true, false)
|
|
|
|
# connect the nodes
|
|
graph_edit.connect_node(
|
|
node.get_name(), port_out.z,
|
|
target_node.get_name(), port_in.y
|
|
)
|
|
print("Connecting %s [%d] to %s [%d]" % [node.get_name(), port_out, target_node.get_name(), port_in])
|
|
|
|
|
|
func calculate_node_depths() -> Dictionary:
|
|
var depths: Dictionary = {}
|
|
depths[0] = 0 # Root is at depth 0
|
|
var state_names = state_machine.state_machine_data["states"].keys()
|
|
for i in range(state_names.size()):
|
|
var state_name = state_names[i]
|
|
var state_data = state_machine.state_machine_data["states"][state_name]
|
|
if state_data.has("transitions"):
|
|
for transition in state_data["transitions"]:
|
|
if transition.has("target"):
|
|
var target_state_name = transition["target"]
|
|
var target_state_index = state_names.find(target_state_name)
|
|
if target_state_index != -1:
|
|
depths[target_state_index] = depths[i] + 1
|
|
return depths
|
|
|
|
|
|
func get_nodes_at_depth(depth: int, depths: Dictionary) -> Array:
|
|
var nodes_at_depth: Array[Variant] = []
|
|
for i in depths.keys():
|
|
if depths[i] == depth:
|
|
nodes_at_depth.append(i)
|
|
return nodes_at_depth
|
|
|