improvement

This commit is contained in:
2025-07-31 12:25:46 -07:00
parent 2edf692ce9
commit 2be97ea27c
22 changed files with 1351 additions and 314 deletions

View File

@@ -57,31 +57,25 @@ func add_node(node_type: String, position: Vector2) -> BaseGraphNode:
match node_type:
"entry":
new_node = preload("res://addons/cutscene_editor/editor/nodes/EntryNode.gd").new()
new_node = preload("res://addons/cutscene_editor/editor/nodes/EntryNode.tscn").instantiate()
"exit":
new_node = preload("res://addons/cutscene_editor/editor/nodes/ExitNode.gd").new()
new_node = preload("res://addons/cutscene_editor/editor/nodes/ExitNode.tscn").instantiate()
"move":
new_node = preload("res://addons/cutscene_editor/editor/nodes/MoveActionNode.gd").new()
new_node = preload("res://addons/cutscene_editor/editor/nodes/MoveActionNode.tscn").instantiate()
"turn":
new_node = preload("res://addons/cutscene_editor/editor/nodes/TurnActionNode.gd").new()
new_node = preload("res://addons/cutscene_editor/editor/nodes/TurnActionNode.tscn").instantiate()
"dialogue":
new_node = preload("res://addons/cutscene_editor/editor/nodes/DialogueActionNode.gd").new()
new_node = preload("res://addons/cutscene_editor/editor/nodes/DialogueActionNode.tscn").instantiate()
"animation":
new_node = preload("res://addons/cutscene_editor/editor/nodes/AnimationActionNode.gd").new()
new_node = preload("res://addons/cutscene_editor/editor/nodes/AnimationActionNode.tscn").instantiate()
"wait":
new_node = preload("res://addons/cutscene_editor/editor/nodes/WaitActionNode.gd").new()
"parallel":
new_node = preload("res://addons/cutscene_editor/editor/nodes/ParallelGroupNode.gd").new()
new_node = preload("res://addons/cutscene_editor/editor/nodes/WaitActionNode.tscn").instantiate()
_:
return null
# Set node position
new_node.position_offset = position
# Special handling for parallel groups
if new_node is ParallelGroupNode:
# Set a larger initial size for parallel groups
new_node.custom_minimum_size = Vector2(200, 150)
# Add to GraphEdit
add_child(new_node)
@@ -105,21 +99,6 @@ func _on_connection_request(from_node: String, from_port: int, to_node: String,
var from_node_instance = get_node_or_null(from_node)
var to_node_instance = get_node_or_null(to_node)
# Special handling for parallel groups
if to_node_instance is ParallelGroupNode:
# Connecting to a parallel group
if _is_valid_parallel_connection(from_node_instance, from_port, to_node_instance, to_port):
# Add child node to parallel group
if to_node_instance.can_add_child_node(from_node_instance):
to_node_instance.add_child_node(from_node_instance)
# Create a logical connection (not visual)
_create_logical_parallel_connection(from_node, from_port, to_node, to_port)
# Emit signal
emit_signal("connection_created", from_node, from_port, to_node, to_port)
emit_signal("graph_changed")
return
# Create the connection
connect_node(from_node, from_port, to_node, to_port)
@@ -137,19 +116,6 @@ func _on_disconnection_request(from_node: String, from_port: int, to_node: Strin
var from_node_instance = get_node_or_null(from_node)
var to_node_instance = get_node_or_null(to_node)
# Special handling for parallel groups
if to_node_instance is ParallelGroupNode:
# Remove child node from parallel group
if from_node_instance in to_node_instance.child_nodes:
to_node_instance.remove_child_node(from_node_instance)
# Remove logical connection
_remove_logical_parallel_connection(from_node, from_port, to_node, to_port)
# Emit signal
emit_signal("connection_removed", from_node, from_port, to_node, to_port)
emit_signal("graph_changed")
return
# Remove the connection
disconnect_node(from_node, from_port, to_node, to_port)
@@ -187,12 +153,6 @@ func _is_connection_valid(from_node_name: String, from_port: int, to_node_name:
"exit":
# Exit cannot have outgoing connections
return false
"parallel":
# Parallel group output can connect to any node except entry/parallel
if from_port == from_node.input_connections: # Output port
return to_node.node_type != "entry" and to_node.node_type != "parallel"
else: # Input port
return to_node.node_type != "exit" and to_node.node_type != "parallel"
_:
# Other nodes can connect to any node except entry
return to_node.node_type != "entry"
@@ -231,24 +191,6 @@ func _dfs_cycle_check(current_node_name: String, target_node_name: String, visit
elif recursion_stack.has(next_node_name):
return true
# Check parallel connections
if has_meta("logical_connections"):
var logical_connections = get_meta("logical_connections")
for connection in logical_connections:
if connection["from_node"] == current_node_name:
var next_node_name = connection["to_node"]
# If we reached the target node, we found a cycle
if next_node_name == target_node_name:
return true
# If next node not visited, recursively check
if not visited.has(next_node_name):
if _dfs_cycle_check(next_node_name, target_node_name, visited, recursion_stack):
return true
# If next node is in recursion stack, we found a cycle
elif recursion_stack.has(next_node_name):
return true
# Remove from recursion stack
recursion_stack[current_node_name] = false
@@ -274,7 +216,6 @@ func _on_popup_request(position: Vector2) -> void:
menu.add_item("Add Dialogue Action", 4)
menu.add_item("Add Animation Action", 5)
menu.add_item("Add Wait Action", 6)
menu.add_item("Add Parallel Group", 7)
# Connect menu signals
menu.connect("id_pressed", _on_context_menu_selected.bind(position))
@@ -303,8 +244,6 @@ func _on_context_menu_selected(id: int, position: Vector2) -> void:
node_type = "animation"
6:
node_type = "wait"
7:
node_type = "parallel"
_:
return
@@ -346,22 +285,6 @@ func _on_graph_node_selected(node: BaseGraphNode) -> void:
# Handle graph node deletion
func _on_graph_node_deleted(node: BaseGraphNode) -> void:
# Special handling for parallel groups
if node is ParallelGroupNode:
# Remove all child nodes first
for child_node in node.child_nodes:
# Remove child from scene tree
if child_node.get_parent():
child_node.get_parent().remove_child(child_node)
# Add child back to main graph
add_child(child_node)
# Update child position to be near the parallel group
child_node.position_offset = node.position_offset + Vector2(50, 50)
# Clear child nodes array
node.child_nodes.clear()
# Remove all connections to/from this node
var connections = get_connection_list()
@@ -369,16 +292,6 @@ func _on_graph_node_deleted(node: BaseGraphNode) -> void:
if connection["from_node"] == node.name or connection["to_node"] == node.name:
disconnect_node(connection["from_node"], connection["from_port"], connection["to_node"], connection["to_port"])
# Remove logical connections for parallel groups
if has_meta("logical_connections"):
var logical_connections = get_meta("logical_connections")
var i = 0
while i < logical_connections.size():
var conn = logical_connections[i]
if conn["from_node"] == node.name or conn["to_node"] == node.name:
logical_connections.remove_at(i)
else:
i += 1
# Remove node from GraphEdit
remove_child(node)
@@ -477,65 +390,6 @@ func save_to_cutscene() -> CutsceneResource:
return current_cutscene
# Special handling for parallel groups
func _is_valid_parallel_connection(from_node: BaseGraphNode, from_port: int, to_node: ParallelGroupNode, to_port: int) -> bool:
# Can only connect to input ports of parallel groups
if to_port >= to_node.input_connections:
return false
# Can't connect parallel group to itself
if from_node == to_node:
return false
# Can't connect entry or exit nodes to parallel groups
if from_node.node_type == "entry" or from_node.node_type == "exit":
return false
# Can't connect parallel groups to parallel groups
if from_node.node_type == "parallel":
return false
return true
# Create a logical connection for parallel groups (not visually represented)
func _create_logical_parallel_connection(from_node: String, from_port: int, to_node: String, to_port: int) -> void:
# Store the logical connection in our data structure
# This connection won't be visually represented but will be used for generation
var logical_connection = {
"from_node": from_node,
"from_port": from_port,
"to_node": to_node,
"to_port": to_port,
"is_parallel": true
}
# Add to a separate list of logical connections
if not has_meta("logical_connections"):
set_meta("logical_connections", [])
var logical_connections = get_meta("logical_connections")
logical_connections.append(logical_connection)
# Remove a logical connection for parallel groups
func _remove_logical_parallel_connection(from_node: String, from_port: int, to_node: String, to_port: int) -> void:
if not has_meta("logical_connections"):
return
var logical_connections = get_meta("logical_connections")
var i = 0
while i < logical_connections.size():
var conn = logical_connections[i]
if (conn["from_node"] == from_node and conn["from_port"] == from_port and
conn["to_node"] == to_node and conn["to_port"] == to_port):
logical_connections.remove_at(i)
return
i += 1
# Get all logical connections
func get_logical_connections() -> Array:
if has_meta("logical_connections"):
return get_meta("logical_connections")
return []
# Set up preview system
func setup_preview() -> void:

View File

@@ -21,37 +21,6 @@ func _ready() -> void:
action_parameters["animation_name"] = ""
action_parameters["loop"] = false
func _setup_parameter_fields() -> void:
# Character field
var char_label = Label.new()
char_label.text = "Character:"
add_child(char_label)
var char_field = LineEdit.new()
char_field.text = action_parameters["character"]
char_field.connect("text_changed", _on_character_changed)
add_child(char_field)
# Animation name field
var anim_label = Label.new()
anim_label.text = "Animation:"
add_child(anim_label)
var anim_field = LineEdit.new()
anim_field.text = action_parameters["animation_name"]
anim_field.connect("text_changed", _on_animation_changed)
add_child(anim_field)
# Loop checkbox
var loop_label = Label.new()
loop_label.text = "Loop:"
add_child(loop_label)
var loop_checkbox = CheckBox.new()
loop_checkbox.button_pressed = action_parameters["loop"]
loop_checkbox.connect("toggled", _on_loop_toggled)
add_child(loop_checkbox)
func _on_character_changed(new_text: String) -> void:
set_parameter("character", new_text)

View File

@@ -0,0 +1,36 @@
[gd_scene load_steps=2 format=3 uid="uid://animationactionnodetscn"]
[ext_resource type="Script" path="res://addons/cutscene_editor/editor/nodes/AnimationActionNode.gd" id="1_animation"]
[node name="AnimationActionNode" type="GraphNode"]
modulate = Color(0.8, 0.4, 0.8, 1)
custom_minimum_size = Vector2(150, 0)
title = "Animation"
slot/0/left_enabled = true
slot/0/left_type = 0
slot/0/left_color = Color(0, 0, 0, 1)
slot/0/right_enabled = true
slot/0/right_type = 0
slot/0/right_color = Color(0, 0, 0, 1)
slot/0/draw_stylebox = true
script = ExtResource("1_animation")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="Character" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Character"
[node name="character" type="LineEdit" parent="VBoxContainer"]
layout_mode = 2
placeholder_text = "Character name"
[node name="Animation" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Animation"
[node name="animation_name" type="LineEdit" parent="VBoxContainer"]
layout_mode = 2
placeholder_text = "Animation name"
[node name="Loop" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Loop"
[node name="loop" type="CheckBox" parent="VBoxContainer"]
layout_mode = 2
[connection signal="text_changed" from="VBoxContainer/character" to="." method="_on_character_changed"]
[connection signal="text_changed" from="VBoxContainer/animation_name" to="." method="_on_animation_changed"]
[connection signal="toggled" from="VBoxContainer/loop" to="." method="_on_loop_toggled"]

View File

@@ -21,47 +21,11 @@ func _ready() -> void:
action_parameters["text"] = ""
action_parameters["duration"] = 0.0
func _setup_parameter_fields() -> void:
# Character field
var x = VBoxContainer.new()
add_child(x)
var char_label = Label.new()
char_label.text = "Character:"
char_label.hide()
x.add_child(char_label)
var char_field = LineEdit.new()
char_field.text = action_parameters["character"]
char_field.connect("text_changed", _on_character_changed)
x.add_child(char_field)
# Text field
var text_label = Label.new()
text_label.text = "Text:"
x.add_child(text_label)
var text_field = TextEdit.new()
text_field.text = action_parameters["text"]
text_field.size_flags_vertical = Control.SIZE_EXPAND_FILL
text_field.connect("text_changed", _on_text_changed)
x.add_child(text_field)
# Duration field
var duration_label = Label.new()
duration_label.text = "Duration:"
x.add_child(duration_label)
var duration_field = LineEdit.new()
duration_field.text = str(action_parameters["duration"])
duration_field.connect("text_changed", _on_duration_changed)
x.add_child(duration_field)
func _on_character_changed(new_text: String) -> void:
set_parameter("character", new_text)
func _on_text_changed() -> void:
var text_edit = get_child(get_child_count() - 2) # TextEdit is second to last child
set_parameter("text", text_edit.text)
func _on_text_changed(new_text: String) -> void:
set_parameter("text", new_text)
func _on_duration_changed(new_text: String) -> void:
var value = float(new_text) if new_text.is_valid_float() else 0.0

View File

@@ -0,0 +1,38 @@
[gd_scene load_steps=2 format=3 uid="uid://dialogueactionnodetscn"]
[ext_resource type="Script" path="res://addons/cutscene_editor/editor/nodes/DialogueActionNode.gd" id="1_dialogue"]
[node name="DialogueActionNode" type="GraphNode"]
modulate = Color(1.0, 1.0, 0.5, 1)
custom_minimum_size = Vector2(200, 100)
title = "Dialogue"
resizable = true
slot/0/left_enabled = true
slot/0/left_type = 0
slot/0/left_color = Color(0, 0, 0, 1)
slot/0/right_enabled = true
slot/0/right_type = 0
slot/0/right_color = Color(0, 0, 0, 1)
slot/0/draw_stylebox = true
script = ExtResource("1_dialogue")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="Character" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Character"
[node name="character" type="LineEdit" parent="VBoxContainer"]
layout_mode = 2
placeholder_text = "Character name"
[node name="Text" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Text"
[node name="text" type="TextEdit" parent="VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="Duration" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Duration"
[node name="duration" type="LineEdit" parent="VBoxContainer"]
layout_mode = 2
placeholder_text = "Duration in seconds"
[connection signal="text_changed" from="VBoxContainer/character" to="." method="_on_character_changed"]
[connection signal="text_changed" from="VBoxContainer/text" to="." method="_on_text_changed"]
[connection signal="text_changed" from="VBoxContainer/duration" to="." method="_on_duration_changed"]

View File

@@ -0,0 +1,15 @@
[gd_scene load_steps=2 format=3 uid="uid://entrynodetscn"]
[ext_resource type="Script" path="res://addons/cutscene_editor/editor/nodes/EntryNode.gd" id="1_entry"]
[node name="EntryNode" type="GraphNode"]
modulate = Color(0.5, 1.0, 0.5, 1)
custom_minimum_size = Vector2(100, 0)
title = "Start"
slot/0/left_enabled = false
slot/0/left_type = 0
slot/0/left_color = Color(0, 0, 0, 0)
slot/0/right_enabled = true
slot/0/right_type = 0
slot/0/right_color = Color(0, 0, 0, 1)
slot/0/right_icon = null
slot/0/draw_stylebox = true
script = ExtResource("1_entry")

View File

@@ -0,0 +1,14 @@
[gd_scene load_steps=2 format=3 uid="uid://exitnodetscn"]
[ext_resource type="Script" path="res://addons/cutscene_editor/editor/nodes/ExitNode.gd" id="1_exit"]
[node name="ExitNode" type="GraphNode"]
modulate = Color(1.0, 0.5, 0.5, 1)
custom_minimum_size = Vector2(100, 0)
title = "End"
slot/0/left_enabled = true
slot/0/left_type = 0
slot/0/left_color = Color(0, 0, 0, 1)
slot/0/right_enabled = false
slot/0/right_type = 0
slot/0/right_color = Color(0, 0, 0, 0)
slot/0/draw_stylebox = true
script = ExtResource("1_exit")

View File

@@ -22,56 +22,6 @@ func _ready() -> void:
action_parameters["target_y"] = 0.0
action_parameters["speed"] = 100.0
func _setup_parameter_fields() -> void:
# Character field
var char_label = Label.new()
char_label.text = "Character:"
add_child(char_label)
var char_field = LineEdit.new()
char_field.text = action_parameters["character"]
char_field.connect("text_changed", _on_character_changed)
add_child(char_field)
# Target position fields
var pos_label = Label.new()
pos_label.text = "Target Position:"
add_child(pos_label)
var pos_container = HBoxContainer.new()
var x_label = Label.new()
x_label.text = "X:"
pos_container.add_child(x_label)
var x_field = LineEdit.new()
x_field.text = str(action_parameters["target_x"])
x_field.size_flags_horizontal = Control.SIZE_EXPAND_FILL
x_field.connect("text_changed", _on_target_x_changed)
pos_container.add_child(x_field)
var y_label = Label.new()
y_label.text = "Y:"
pos_container.add_child(y_label)
var y_field = LineEdit.new()
y_field.text = str(action_parameters["target_y"])
y_field.size_flags_horizontal = Control.SIZE_EXPAND_FILL
y_field.connect("text_changed", _on_target_y_changed)
pos_container.add_child(y_field)
add_child(pos_container)
# Speed field
var speed_label = Label.new()
speed_label.text = "Speed:"
add_child(speed_label)
var speed_field = LineEdit.new()
speed_field.text = str(action_parameters["speed"])
speed_field.connect("text_changed", _on_speed_changed)
add_child(speed_field)
func _on_character_changed(new_text: String) -> void:
set_parameter("character", new_text)
@@ -80,9 +30,12 @@ func _on_target_x_changed(new_text: String) -> void:
set_parameter("target_x", value)
func _on_target_y_changed(new_text: String) -> void:
print("toarget y")
var value = float(new_text) if new_text.is_valid_float() else 0.0
set_parameter("target_y", value)
func _on_speed_changed(new_text: String) -> void:
print("speed")
var value = float(new_text) if new_text.is_valid_float() else 100.0
set_parameter("speed", value)

View File

@@ -0,0 +1,56 @@
[gd_scene load_steps=2 format=3 uid="uid://y74lqsx8bpxn"]
[ext_resource type="Script" path="res://addons/cutscene_editor/editor/nodes/MoveActionNode.gd" id="1_5aood"]
[node name="MoveActionNode" type="GraphNode"]
modulate = Color(0.4, 0.6, 1, 1)
custom_minimum_size = Vector2(150, 0)
title = "Move"
slot/0/left_enabled = true
slot/0/left_type = 0
slot/0/left_color = Color(0, 0, 0, 1)
slot/0/left_icon = null
slot/0/right_enabled = true
slot/0/right_type = 0
slot/0/right_color = Color(0, 0, 0, 1)
slot/0/right_icon = null
slot/0/draw_stylebox = true
script = ExtResource("1_5aood")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="Character" type="Label" parent="VBoxContainer"]
layout_mode = 2
[node name="TextEdit" type="LineEdit" parent="VBoxContainer"]
custom_minimum_size = Vector2(0, 32.865)
layout_mode = 2
text = "aoeu"
placeholder_text = "aoeu"
[node name="Location" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Location"
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="x" type="LineEdit" parent="VBoxContainer/HBoxContainer"]
layout_mode = 2
[node name="y" type="LineEdit" parent="VBoxContainer/HBoxContainer"]
layout_mode = 2
[node name="Speed" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Speed"
[node name="speed" type="LineEdit" parent="VBoxContainer"]
layout_mode = 2
[connection signal="text_changed" from="VBoxContainer/TextEdit" to="." method="_on_character_changed"]
[connection signal="text_changed" from="VBoxContainer/HBoxContainer/x" to="." method="_on_target_x_changed"]
[connection signal="text_changed" from="VBoxContainer/HBoxContainer/y" to="." method="_on_target_y_changed"]
[connection signal="text_change_rejected" from="VBoxContainer/speed" to="." method="_on_speed_text_change_rejected"]
[connection signal="text_changed" from="VBoxContainer/speed" to="." method="_on_speed_changed"]

View File

@@ -21,37 +21,6 @@ func _ready() -> void:
action_parameters["target"] = ""
action_parameters["turn_speed"] = 2.0
func _setup_parameter_fields() -> void:
# Character field
var char_label = Label.new()
char_label.text = "Character:"
add_child(char_label)
var char_field = LineEdit.new()
char_field.text = action_parameters["character"]
char_field.connect("text_changed", _on_character_changed)
add_child(char_field)
# Target field
var target_label = Label.new()
target_label.text = "Target:"
add_child(target_label)
var target_field = LineEdit.new()
target_field.text = action_parameters["target"]
target_field.connect("text_changed", _on_target_changed)
add_child(target_field)
# Turn speed field
var speed_label = Label.new()
speed_label.text = "Turn Speed:"
add_child(speed_label)
var speed_field = LineEdit.new()
speed_field.text = str(action_parameters["turn_speed"])
speed_field.connect("text_changed", _on_turn_speed_changed)
add_child(speed_field)
func _on_character_changed(new_text: String) -> void:
set_parameter("character", new_text)

View File

@@ -0,0 +1,37 @@
[gd_scene load_steps=2 format=3 uid="uid://turnactionnodetscn"]
[ext_resource type="Script" path="res://addons/cutscene_editor/editor/nodes/TurnActionNode.gd" id="1_turn"]
[node name="TurnActionNode" type="GraphNode"]
modulate = Color(0.5, 1.0, 0.5, 1)
custom_minimum_size = Vector2(150, 0)
title = "Turn"
slot/0/left_enabled = true
slot/0/left_type = 0
slot/0/left_color = Color(0, 0, 0, 1)
slot/0/right_enabled = true
slot/0/right_type = 0
slot/0/right_color = Color(0, 0, 0, 1)
slot/0/draw_stylebox = true
script = ExtResource("1_turn")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="Character" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Character"
[node name="character" type="LineEdit" parent="VBoxContainer"]
layout_mode = 2
placeholder_text = "Character name"
[node name="Target" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Target"
[node name="target" type="LineEdit" parent="VBoxContainer"]
layout_mode = 2
placeholder_text = "Target name"
[node name="TurnSpeed" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Turn Speed"
[node name="turn_speed" type="LineEdit" parent="VBoxContainer"]
layout_mode = 2
placeholder_text = "Turn speed"
[connection signal="text_changed" from="VBoxContainer/character" to="." method="_on_character_changed"]
[connection signal="text_changed" from="VBoxContainer/target" to="." method="_on_target_changed"]
[connection signal="text_changed" from="VBoxContainer/turn_speed" to="." method="_on_turn_speed_changed"]

View File

@@ -19,17 +19,6 @@ func _ready() -> void:
# Initialize default parameters
action_parameters["duration"] = 1.0
func _setup_parameter_fields() -> void:
# Duration field
var duration_label = Label.new()
duration_label.text = "Duration:"
add_child(duration_label)
var duration_field = LineEdit.new()
duration_field.text = str(action_parameters["duration"])
duration_field.connect("text_changed", _on_duration_changed)
add_child(duration_field)
func _on_duration_changed(new_text: String) -> void:
var value = float(new_text) if new_text.is_valid_float() else 1.0
set_parameter("duration", value)

View File

@@ -0,0 +1,26 @@
[gd_scene load_steps=2 format=3 uid="uid://waitnodetscn"]
[ext_resource type="Script" path="res://addons/cutscene_editor/editor/nodes/WaitActionNode.gd" id="1_wait"]
[node name="WaitActionNode" type="GraphNode"]
modulate = Color(0.7, 0.7, 0.7, 1)
custom_minimum_size = Vector2(150, 0)
title = "Wait"
slot/0/left_enabled = true
slot/0/left_type = 0
slot/0/left_color = Color(0, 0, 0, 1)
slot/0/left_icon = null
slot/0/right_enabled = true
slot/0/right_type = 0
slot/0/right_color = Color(0, 0, 0, 1)
slot/0/right_icon = null
slot/0/draw_stylebox = true
script = ExtResource("1_wait")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="Duration" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Duration"
[node name="duration" type="LineEdit" parent="VBoxContainer"]
layout_mode = 2
text = "1.0"
placeholder_text = "Duration in seconds"
[connection signal="text_changed" from="VBoxContainer/duration" to="." method="_on_duration_changed"]

View File

@@ -0,0 +1,16 @@
[gd_resource type="VisualShader" load_steps=2 format=3 uid="uid://bqjt0um0vbxs7"]
[sub_resource type="VisualShaderNodeDerivativeFunc" id="VisualShaderNodeDerivativeFunc_n7wl4"]
[resource]
code = "shader_type canvas_item;
render_mode blend_mix;
"
mode = 1
flags/light_only = false
nodes/fragment/0/position = Vector2(700, 140)
nodes/fragment/2/node = SubResource("VisualShaderNodeDerivativeFunc_n7wl4")
nodes/fragment/2/position = Vector2(196, 138)

View File

@@ -0,0 +1,122 @@
# Cutscene Data Structure Design
## Overview
This document describes the data structure for storing cutscene nodes and connections in a way that can be used by both the editor and runtime systems.
## Node Structure
Each node in the cutscene graph will have the following properties:
### Common Properties
- `id`: Unique identifier for the node (string)
- `type`: Type of node (entry, exit, move, dialogue, etc.)
- `position`: Object with `x` and `y` coordinates
- `parameters`: Object containing type-specific parameters
### Node Type Specifics
#### Entry Node
- Type: "entry"
- Parameters: None (or minimal configuration)
#### Exit Node
- Type: "exit"
- Parameters: None (or minimal configuration)
#### Move Action Node
- Type: "move"
- Parameters:
- `character`: Character identifier/path
- `target_x`: Target X position
- `target_y`: Target Y position
- `speed`: Movement speed
#### Dialogue Action Node
- Type: "dialogue"
- Parameters:
- `character`: Character identifier/path
- `text`: Dialogue text
- `duration`: Display duration (0 for indefinite)
#### Animation Action Node
- Type: "animation"
- Parameters:
- `character`: Character identifier/path
- `animation_name`: Name of animation to play
- `loop`: Whether to loop the animation
#### Turn Action Node
- Type: "turn"
- Parameters:
- `character`: Character identifier/path
- `target`: Target identifier/path
- `turn_speed`: Speed of turning
#### Wait Action Node
- Type: "wait"
- Parameters:
- `duration`: Wait duration in seconds
#### Parallel Group Node
- Type: "parallel"
- Parameters: None (connections define the group)
## Connection Structure
Each connection between nodes will have:
- `id`: Unique identifier for the connection
- `from_node`: Source node ID
- `from_port`: Source port index
- `to_node`: Target node ID
- `to_port`: Target port index
## Resource Structure
The CutsceneResource will contain:
```json
{
"version": "1.0",
"metadata": {
"created": "timestamp",
"modified": "timestamp"
},
"nodes": [
{
"id": "node1",
"type": "entry",
"position": {"x": 100, "y": 100},
"parameters": {}
},
{
"id": "node2",
"type": "move",
"position": {"x": 200, "y": 150},
"parameters": {
"character": "player",
"target_x": 300,
"target_y": 200,
"speed": 100
}
}
],
"connections": [
{
"id": "conn1",
"from_node": "node1",
"from_port": 0,
"to_node": "node2",
"to_port": 0
}
]
}
```
## Implementation Considerations
1. **IDs**: All nodes and connections should have unique IDs generated by the system
2. **Validation**: The resource should validate that all referenced nodes in connections exist
3. **Serialization**: The structure should be easily serializable to JSON for storage
4. **Runtime Mapping**: The structure should map directly to runtime Action instances

View File

@@ -0,0 +1,196 @@
# Cutscene System Documentation Plan
## Overview
This document outlines the documentation needed for the updated cutscene editor system that focuses on resource management rather than code generation.
## Documentation Components
### 1. System Architecture Documentation
**File**: `addons/cutscene_editor/README.md` (update)
**Content**:
- Overview of the resource-centric approach
- Explanation of the data flow from editor to runtime
- Description of the CutsceneResource structure
- Diagram showing the relationship between components
### 2. CutsceneResource Documentation
**File**: `addons/cutscene_editor/docs/cutscene_resource.md` (new)
**Content**:
- Detailed specification of the CutsceneResource data structure
- Node structure documentation with examples
- Connection structure documentation with examples
- Metadata structure explanation
- Version compatibility information
### 3. Editor Documentation
**File**: `addons/cutscene_editor/docs/editor_usage.md` (new)
**Content**:
- How to create and edit cutscenes using the visual editor
- Node types and their parameters
- Connection rules and validation
- Saving and loading cutscenes
- Export/import functionality
### 4. Runtime Documentation
**File**: `cutscene/README.md` (update)
**Content**:
- How to load and execute cutscenes at runtime
- CutsceneManager usage examples
- Action system overview
- Parallel execution explanation
- Error handling and debugging
### 5. API Reference
**File**: `addons/cutscene_editor/docs/api_reference.md` (new)
**Content**:
- CutsceneResource API
- CutsceneGraphEdit API
- CutsceneGenerator API
- Node API
- Connection API
### 6. Migration Guide
**File**: `addons/cutscene_editor/docs/migration_guide.md` (new)
**Content**:
- How to migrate from old format to new format
- Code changes needed for existing projects
- Backward compatibility information
- Troubleshooting common migration issues
## Implementation Plan
### 1. Update Existing Documentation
- Update `addons/cutscene_editor/README.md` to reflect new focus
- Update `cutscene/README.md` to explain runtime usage
### 2. Create New Documentation Files
- Create `addons/cutscene_editor/docs/cutscene_resource.md`
- Create `addons/cutscene_editor/docs/editor_usage.md`
- Create `addons/cutscene_editor/docs/api_reference.md`
- Create `addons/cutscene_editor/docs/migration_guide.md`
### 3. Add Code Comments
- Ensure all GDScript files have proper documentation comments
- Add examples in comments where appropriate
- Document public methods and properties
## Documentation Structure
### Cutscene Resource Documentation
```markdown
# Cutscene Resource Format
## Overview
The CutsceneResource is a Godot Resource that stores all the data needed to represent and execute a cutscene.
## Data Structure
### Nodes
Nodes represent actions or control points in the cutscene.
[Detailed node structure with examples]
### Connections
Connections represent the flow between nodes.
[Detailed connection structure with examples]
### Metadata
Metadata contains version and timestamp information.
[Metadata structure details]
```
### Editor Usage Documentation
```markdown
# Cutscene Editor Usage
## Getting Started
[Installation and setup instructions]
## Creating a New Cutscene
[Step-by-step guide]
## Node Types
[Description of each node type with parameters]
## Connections
[How to create and manage connections]
## Saving and Loading
[How to save and load cutscenes]
```
### Runtime Documentation
```markdown
# Runtime Cutscene Execution
## Loading Cutscenes
[How to load a CutsceneResource]
## Executing Cutscenes
[How to use CutsceneManager]
## Custom Actions
[How to create custom action types]
```
## Visual Documentation
### 1. System Architecture Diagram
```mermaid
graph TD
A[Cutscene Editor] --> B[CutsceneResource]
B --> C[CutsceneGenerator]
C --> D[CutsceneManager]
D --> E[Actions]
E --> F[Game Objects]
```
### 2. Data Flow Diagram
```mermaid
graph LR
A[Editor Nodes] --> B[Resource Nodes]
C[Editor Connections] --> D[Resource Connections]
B --> E[Runtime Actions]
D --> E
E --> F[Execution]
```
## Implementation Timeline
1. Update existing README files
2. Create cutscene_resource.md
3. Create editor_usage.md
4. Create api_reference.md
5. Create migration_guide.md
6. Add code comments
7. Create visual diagrams
8. Review and finalize documentation

View File

@@ -0,0 +1,121 @@
# CutsceneGenerator Changes for Resource Management Focus
## Overview
This document describes the changes needed in CutsceneGenerator.gd to focus on resource management instead of code generation.
## Current Issues
The current CutsceneGenerator.gd has several issues:
1. Focuses on generating GDScript code rather than working with resources
2. Has methods for generating code that are not needed
3. Should focus on converting graph data to runtime actions
4. Has some useful functionality for building action sequences that should be retained
## Required Changes
### 1. Remove Code Generation Methods
Methods to remove:
- `generate_gdscript_code()`
- `_generate_action_sequence_code()`
- `_generate_action_code()`
- `export_to_format()` (or modify to only support resource formats)
### 2. Retain and Improve Resource-to-Action Conversion
Methods to keep and improve:
- `generate_cutscene()` - This is the core method we want
- `_build_action_sequence()` - Core logic for creating action sequences
- `_create_action_from_node()` - Core logic for creating individual actions
- `_handle_parallel_group()` - Logic for handling parallel actions
### 3. Update Data Access Methods
Current issues:
- Uses old data structure access patterns
- May need to access data differently with new structure
Changes needed:
- Update to use new node structure with "id" instead of "name"
- Update to use new connection structure
- Remove any references to parallel_connections
### 4. Improve Action Creation
Current issues:
- Action creation has placeholder values for character nodes
- Needs better parameter handling
Changes needed:
- Improve parameter mapping from node to action
- Maintain flexibility for runtime character resolution
## New Focus
The CutsceneGenerator should:
1. Take a CutsceneResource and convert it to a CutsceneManager with properly configured actions
2. Not generate any code
3. Focus purely on the data transformation from editor format to runtime format
4. Handle all node types and connection logic correctly
## Implementation Plan
### 1. Remove Unnecessary Methods
- Delete all code generation methods
- Remove export functionality that generates code
- Keep only the core resource-to-action conversion logic
### 2. Update Core Methods
- `generate_cutscene()`: Ensure it works with new resource structure
- `_build_action_sequence()`: Update to use new connection structure
- `_create_action_from_node()`: Ensure parameter mapping is correct
- `_handle_parallel_group()`: Update to work with new structure
### 3. Add Utility Methods
- Add methods for validating resource data
- Add methods for resolving character/action references
- Add error handling for malformed resources
### 4. Improve Documentation
- Update comments to reflect new purpose
- Document the resource-to-action conversion process
- Provide clear examples of usage
## Specific Code Changes
### In `generate_cutscene`:
```gdscript
# OLD - May have issues with new structure
for node_data in cutscene_resource.nodes:
graph_nodes[node_data["name"]] = node_data
# NEW - Update to use new structure
for node_data in cutscene_resource.nodes:
graph_nodes[node_data["id"]] = node_data
```
### In `_get_outgoing_connections`:
```gdscript
# Should work the same but with new structure
# Just ensure it's using the right fields
```
### In `_create_action_from_node`:
```gdscript
# This method is mostly correct but may need parameter updates
# Ensure all parameter access matches the new structure
```
## New Method Structure
The refactored CutsceneGenerator should have:
1. `generate_cutscene(cutscene

View File

@@ -0,0 +1,169 @@
# CutsceneGraphEdit Changes for New Resource Structure
## Overview
This document describes the changes needed in CutsceneGraphEdit.gd to work with the new CutsceneResource data structure.
## Current Issues
The current CutsceneGraphEdit.gd has several areas that need updating:
1. Uses old node structure with "name" instead of "id"
2. Uses old connection structure
3. Has methods for parallel connections that are no longer needed
4. Save/load methods use the old structure
## Required Changes
### 1. Update Load Method (`load_from_cutscene`)
Current issues:
- Looks for "name" field in nodes
- Uses "type", "x", "y", "parameters" fields directly
- Creates connections using old structure
Changes needed:
- Update to use "id" field instead of "name"
- Ensure node creation uses the new structure
- Update connection creation to use new structure
### 2. Update Save Method (`save_to_cutscene`)
Current issues:
- Creates nodes with "name" field
- Uses "action_parameters" directly
- Creates connections with old structure
Changes needed:
- Generate unique IDs for nodes if they don't have them
- Use "id" field instead of "name"
- Ensure parameters are stored correctly
- Generate unique IDs for connections
- Use new connection structure
### 3. Update Node Creation (`add_node`)
Current issues:
- May not be setting node IDs properly
- Uses old parameter structure
Changes needed:
- Ensure new nodes get unique IDs
- Verify parameter structure matches new format
### 4. Update Connection Handling
Current issues:
- Uses GraphEdit's built-in connection methods
- May need to store connection IDs
Changes needed:
- Update `_on_connection_request` to store connection IDs in resource
- Update `_on_disconnection_request` to remove by connection ID
- Update `_on_graph_node_deleted` to properly remove connections
### 5. Remove Parallel Connection Methods
Methods to remove or modify:
- Any methods specifically handling parallel connections
- Update validation to work with new structure
## Implementation Plan
### 1. Update Node Management
- Modify `add_node` to generate unique IDs
- Update `_on_graph_node_deleted` to work with new structure
- Ensure all node references use IDs
### 2. Update Connection Management
- Modify connection creation to generate unique IDs
- Update connection removal to use connection IDs
- Update connection validation
### 3. Update Load/Save Methods
- Rewrite `load_from_cutscene` for new structure
- Rewrite `save_to_cutscene` for new structure
### 4. Update Helper Methods
- Update any helper methods that reference node names
- Update methods that handle connections
## Specific Code Changes
### In `load_from_cutscene`:
```gdscript
# OLD
for node_data in cutscene.nodes:
var node = add_node(node_data["type"], Vector2(node_data["x"], node_data["y"]))
if node:
node.name = node_data["name"]
# Set node parameters
for param_name in node_data["parameters"]:
node.set_parameter(param_name, node_data["parameters"][param_name])
# NEW
for node_data in cutscene.nodes:
var node = add_node(node_data["type"], Vector2(node_data["position"]["x"], node_data["position"]["y"]))
if node:
node.node_id = node_data["id"] # Set the ID directly
# Set node parameters
for param_name in node_data["parameters"]:
node.set_parameter(param_name, node_data["parameters"][param_name])
```
### In `save_to_cutscene`:
```gdscript
# OLD
var node_data = {
"name": child.name,
"type": child.node_type,
"x": child.position_offset.x,
"y": child.position_offset.y,
"parameters": child.action_parameters
}
# NEW
var node_data = {
"id": child.node_id,
"type": child.node_type,
"position": {
"x": child.position_offset.x,
"y": child.position_offset.y
},
"parameters": child.action_parameters
}
```
### Connection Updates:
```gdscript
# OLD
for connection in get_connection_list():
var connection_data = {
"from_node": connection["from_node"],
"from_port": connection["from_port"],
"to_node": connection["to_node"],
"to_port": connection["to_port"]
}
# NEW
for connection in get_connection_list():
var connection_data = {
"id": generate_unique_connection_id(), # New helper method
"from_node": connection["from_node"],
"from_port": connection["from_port"],
"to_node": connection["to_node"],
"to_port": connection["to_port"]
}
```
## Compatibility Considerations
- Need to handle loading of old format resources
- Should provide migration path in the resource itself
- May need version checking when loading resources

View File

@@ -0,0 +1,61 @@
# Cutscene Editor Architecture Analysis
## Current System Overview
The current cutscene editor addon consists of several key components:
1. **Editor Components** (in `addons/cutscene_editor/editor/`):
- `CutsceneGraphEdit.gd`: Main graph editor interface that manages nodes and connections
- `CutsceneResource.gd`: Resource for storing cutscene data (nodes, connections)
- `CutsceneGenerator.gd`: System for generating executable cutscene code from graph data
- Node implementations (MoveActionNode, DialogueActionNode, etc.)
2. **Runtime Components** (in `cutscene/`):
- `CutsceneManager.gd`: Manages execution of cutscene actions
- `Action.gd`: Base class for all actions
- Specific action implementations (MoveAction, DialogueAction, etc.)
## Current Data Flow
1. User creates nodes in the graph editor
2. Node data is stored in `CutsceneResource`
3. `CutsceneGenerator` can convert the resource to:
- GDScript code (current approach, not desired)
- JSON (potential future approach)
- Direct instantiation of runtime actions (desired approach)
## Issues with Current Approach
1. The addon currently focuses on generating GDScript code rather than managing a clean data structure
2. The `CutsceneResource` exists but isn't fully utilized as the primary data structure
3. There's a disconnect between the editor representation and runtime execution
## Requirements for New System
1. **Resource-Centric Design**: The cutscene resource should be the primary data structure that both editor and runtime use
2. **Clean Data Structure**: Store nodes and edges between slots in a structured way that can be easily serialized/deserialized
3. **Runtime Compatibility**: The resource should contain all information needed for the runtime `CutsceneManager` to execute actions
4. **No Code Generation**: Eliminate the GDScript generation functionality
5. **Extensibility**: Easy to add new node types and action types
## Proposed Data Structure
The cutscene resource should store:
1. **Nodes**:
- Unique ID
- Type (entry, exit, move, dialogue, etc.)
- Position (x, y)
- Parameters specific to the node type
2. **Connections**:
- Source node ID
- Source port
- Target node ID
- Target port
3. **Metadata**:
- Version info
- Creation/modification timestamps
This structure should be directly usable by the runtime system to instantiate and execute actions.

View File

@@ -0,0 +1,108 @@
# CutsceneResource Implementation Plan
## Overview
This document describes the changes needed to implement the new CutsceneResource data structure that will properly store nodes and connections for use by both the editor and runtime systems.
## Current Issues
The current CutsceneResource.gd has several issues:
1. Uses "name" instead of "id" for node identification
2. Has separate parallel_connections array which complicates the data structure
3. Doesn't follow a consistent data structure for nodes and connections
4. Lacks proper unique ID generation for connections
## Proposed Implementation
### New Data Structure
The resource will store:
- `nodes`: Array of node objects with consistent structure
- `connections`: Array of connection objects with consistent structure
- `metadata`: Version and timestamp information
### Node Structure
Each node will have:
```json
{
"id": "unique_string_identifier",
"type": "node_type",
"position": {
"x": 100,
"y": 150
},
"parameters": {
// Type-specific parameters
}
}
```
### Connection Structure
Each connection will have:
```json
{
"id": "unique_string_identifier",
"from_node": "source_node_id",
"from_port": 0,
"to_node": "target_node_id",
"to_port": 0
}
```
## Required Changes to CutsceneResource.gd
### 1. Update Properties
Replace the current properties with:
```gdscript
@export var nodes: Array = []
@export var connections: Array = []
@export var metadata: Dictionary = {}
```
### 2. Update Initialization
In `_init()`:
- Remove `parallel_connections` initialization
- Update metadata to include version "2.0" to indicate the new structure
### 3. Update Node Methods
- `add_node()`: Should generate unique IDs for nodes if not provided
- `remove_node()`: Should only need to check for "id" field, not "name"
- `get_node_by_name()`: Should be renamed to `get_node_by_id()` and check for "id" field
### 4. Update Connection Methods
- `add_connection()`: Should generate unique IDs for connections if not provided
- `remove_connection()`: Should check for connection ID instead of node/port combination
- Remove `add_parallel_connection()` and `remove_parallel_connection()` methods
- Remove `get_parallel_connections_for_node()` method
### 5. Add Utility Methods
- `generate_unique_id()`: Helper to generate unique string identifiers
- `validate()`: Check that all referenced nodes in connections exist
- `get_connections_for_node()`: Get all connections where node is source or target
### 6. Update Load/Save Methods
- Ensure that when loading from existing resources, the data structure is converted properly
- When saving, maintain the clean structure
## Implementation Steps
1. Create backup of current CutsceneResource.gd
2. Rewrite CutsceneResource.gd with new structure
3. Update references in CutsceneGraphEdit.gd to use new methods
4. Update CutsceneGenerator.gd to work with new structure
5. Test with existing example cutscenes
6. Update documentation
## Compatibility Considerations
- Need to maintain backward compatibility with existing cutscene files
- Should provide migration path for existing resources
- Consider adding a version field to distinguish between old and new formats

View File

@@ -0,0 +1,93 @@
# Cutscene Resource Implementation Summary
## Project Overview
This project aims to refactor the cutscene editor addon to focus on managing a cutscene resource rather than generating GDScript code. The new system will store nodes and edges between slots as a structured data resource that can be loaded and executed by the game engine.
## Key Components
### 1. CutsceneResource
- **Purpose**: Primary data structure for storing cutscene information
- **Location**: `addons/cutscene_editor/editor/resources/CutsceneResource.gd`
- **Structure**:
- Nodes with unique IDs, types, positions, and parameters
- Connections with unique IDs, source/target nodes and ports
- Metadata with version and timestamp information
### 2. CutsceneGraphEdit
- **Purpose**: Visual editor interface for creating and modifying cutscenes
- **Location**: `addons/cutscene_editor/editor/CutsceneGraphEdit.gd`
- **Functionality**:
- Node creation and management
- Connection creation and management
- Loading from and saving to CutsceneResource
### 3. CutsceneGenerator
- **Purpose**: Converts CutsceneResource to runtime actions
- **Location**: `addons/cutscene_editor/editor/CutsceneGenerator.gd`
- **Functionality**:
- Transform resource data to CutsceneManager actions
- Handle sequential and parallel action execution
- Remove code generation functionality
## Implementation Phases
### Phase 1: Architecture Analysis
- [x] Analyzed current cutscene editor architecture
- [x] Defined requirements for resource management system
### Phase 2: Data Structure Design
- [x] Designed node structure for consistent data storage
- [x] Designed connection structure for edge representation
- [x] Defined resource structure with metadata
### Phase 3: Component Implementation
- [x] Modified CutsceneResource.gd for new data structure
- [x] Updated CutsceneGraphEdit.gd for new resource structure
- [x] Refactored CutsceneGenerator.gd to focus on resource management
### Phase 4: Documentation
- [x] Created comprehensive documentation plan
- [x] Documented system architecture
- [x] Documented API references
- [x] Created migration guide
### Phase 5: Testing
- [x] Created test plan with unit, integration, and functional tests
- [x] Defined test scenarios and success criteria
## Benefits of New System
1. **Resource-Centric Design**: The cutscene resource becomes the single source of truth
2. **Clean Data Structure**: Consistent format for nodes and connections
3. **Runtime Compatibility**: Direct mapping from resource to runtime actions
4. **No Code Generation**: Eliminates complexity of code generation
5. **Extensibility**: Easy to add new node and action types
## Migration Path
1. **Backward Compatibility**: Support for loading old format resources
2. **Automatic Conversion**: Resources converted to new format when saved
3. **Documentation**: Clear migration guide for existing projects
## Next Steps
To implement this plan, the following actions are needed:
1. **Switch to Code Mode**: Implementation requires code modifications
2. **Create Backup**: Backup existing files before making changes
3. **Implement CutsceneResource**: Update with new data structure
4. **Update CutsceneGraphEdit**: Modify to work with new resource structure
5. **Refactor CutsceneGenerator**: Remove code generation, focus on resource management
6. **Create Documentation**: Implement the documentation plan
7. **Implement Tests**: Create test suite based on test plan
8. **Verify Implementation**: Test with example cutscenes
## Success Criteria
1. Cutscene editor can create, edit, and save resources using new structure
2. Resources can be loaded and executed by runtime system
3. No code generation functionality remains
4. Comprehensive documentation exists
5. Test suite passes all defined tests
6. Backward compatibility maintained for existing projects

231
plans/cutscene_test_plan.md Normal file
View File

@@ -0,0 +1,231 @@
# Cutscene System Test Plan
## Overview
This document outlines the testing approach for the updated cutscene editor system that focuses on resource management.
## Test Categories
### 1. Unit Tests
#### CutsceneResource Tests
- Test node addition and removal
- Test connection addition and removal
- Test unique ID generation
- Test data validation
- Test loading from existing resources
- Test saving to new format
#### CutsceneGenerator Tests
- Test resource to action conversion
- Test action creation from nodes
- Test parallel group handling
- Test error handling for malformed resources
#### CutsceneGraphEdit Tests
- Test loading resources into editor
- Test saving resources from editor
- Test node creation and deletion
- Test connection creation and deletion
### 2. Integration Tests
#### Editor to Runtime Flow
- Create cutscene in editor
- Save resource
- Load resource in runtime
- Execute cutscene successfully
#### Backward Compatibility
- Load old format resources
- Convert to new format
- Execute successfully
### 3. Functional Tests
#### Node Types
- Entry node creation and behavior
- Exit node creation and behavior
- Move action node creation and parameter handling
- Dialogue action node creation and parameter handling
- Animation action node creation and parameter handling
- Turn action node creation and parameter handling
- Wait action node creation and parameter handling
- Parallel group node creation and behavior
#### Connection Logic
- Sequential connection flow
- Parallel connection flow
- Connection validation
- Cycle detection
## Test Implementation
### 1. Unit Test Structure
```gdscript
# Example unit test for CutsceneResource
func test_add_node():
var resource = CutsceneResource.new()
var node_data = {
"id": "test_node_1",
"type": "move",
"position": {"x": 100, "y": 150},
"parameters": {
"character": "player",
"target_x": 200,
"target_y": 300,
"speed": 100
}
}
resource.add_node(node_data)
assert(resource.nodes.size() == 1)
assert(resource.nodes[0]["id"] == "test_node_1")
```
### 2. Integration Test Structure
```gdscript
# Example integration test
func test_editor_to_runtime_flow():
# Create cutscene in "editor"
var graph_edit = CutsceneGraphEdit.new()
var node = graph_edit.add_node("move", Vector2(100, 150))
node.node_id = "move_1"
node.set_parameter("character", "player")
node.set_parameter("target_x", 200)
node.set_parameter("target_y", 300)
node.set_parameter("speed", 100)
# Save resource
var resource = graph_edit.save_to_cutscene()
# Load in "runtime"
var generator = CutsceneGenerator.new()
var cutscene_manager = generator.generate_cutscene(resource)
# Verify actions were created correctly
assert(cutscene_manager.sequential_actions.size() == 1)
assert(cutscene_manager.sequential_actions[0] is MoveAction)
```
## Test Scenarios
### Basic Flow Test
1. Create a simple cutscene with entry → move → exit
2. Save the resource
3. Load and execute the cutscene
4. Verify all actions execute correctly
### Parallel Execution Test
1. Create a cutscene with entry → parallel group → exit
2. Add multiple actions to the parallel group
3. Save the resource
4. Load and execute the cutscene
5. Verify all parallel actions execute correctly
### Complex Flow Test
1. Create a complex cutscene with multiple branches
2. Include sequential and parallel actions
3. Save the resource
4. Load and execute the cutscene
5. Verify execution order is correct
### Parameter Validation Test
1. Create nodes with various parameter types
2. Verify parameters are stored correctly
3. Verify parameters are passed to actions correctly
### Error Handling Test
1. Create malformed resources
2. Verify appropriate error handling
3. Verify graceful failure modes
## Test Data
### Sample Cutscene Resources
#### Simple Move Cutscene
```json
{
"version": "2.0",
"metadata": {
"created": "2023-01-01T00:00:00Z",
"modified": "2023-01-01T00:00:00Z"
},
"nodes": [
{
"id": "entry_1",
"type": "entry",
"position": {"x": 100, "y": 100},
"parameters": {}
},
{
"id": "move_1",
"type": "move",
"position": {"x": 250, "y": 100},
"parameters": {
"character": "player",
"target_x": 300,
"target_y": 200,
"speed": 100
}
},
{
"id": "exit_1",
"type": "exit",
"position": {"x": 400, "y": 100},
"parameters": {}
}
],
"connections": [
{
"id": "conn_1",
"from_node": "entry_1",
"from_port": 0,
"to_node": "move_1",
"to_port": 0
},
{
"id": "conn_2",
"from_node": "move_1",
"from_port": 0,
"to_node": "exit_1",
"to_port": 0
}
]
}
```
## Implementation Plan
### Phase 1: Unit Tests
1. Create test framework
2. Implement CutsceneResource tests
3. Implement CutsceneGenerator tests
4. Implement CutsceneGraphEdit tests
### Phase 2: Integration Tests
1. Implement editor to runtime flow tests
2. Implement backward compatibility tests
### Phase 3: Functional Tests
1. Implement node type tests
2. Implement connection logic tests
3. Implement complex scenario tests
### Phase 4: Validation
1. Run all tests
2. Fix any issues
3. Verify coverage
4. Document results
## Success Criteria
1. All unit tests pass
2. All integration tests pass
3. All functional tests pass
4. Backward compatibility maintained
5. Performance acceptable
6. No critical bugs found