progress
This commit is contained in:
@@ -2,32 +2,24 @@
|
||||
class_name CutsceneGenerator
|
||||
extends Object
|
||||
|
||||
# System for generating executable cutscene code from graph data
|
||||
# System for generating executable cutscene actions from graph data
|
||||
|
||||
# Properties
|
||||
var graph_nodes: Dictionary = {} # Map of node names to node data
|
||||
var graph_nodes: Dictionary = {} # Map of node IDs to node data
|
||||
var connections: Array = [] # List of connection data
|
||||
var logical_connections: Array = [] # Logical connections for parallel groups
|
||||
var node_instances: Dictionary = {} # Map of node names to instantiated nodes
|
||||
|
||||
# Generate a cutscene from graph data
|
||||
func generate_cutscene(cutscene_resource: CutsceneResource) -> CutsceneManager:
|
||||
# Clear previous data
|
||||
graph_nodes.clear()
|
||||
connections.clear()
|
||||
logical_connections.clear()
|
||||
node_instances.clear()
|
||||
|
||||
# Load graph data
|
||||
for node_data in cutscene_resource.nodes:
|
||||
graph_nodes[node_data["name"]] = node_data
|
||||
graph_nodes[node_data["id"]] = node_data
|
||||
|
||||
connections = cutscene_resource.connections
|
||||
|
||||
# Load logical connections if they exist
|
||||
if cutscene_resource.has_method("get_parallel_connections"):
|
||||
logical_connections = cutscene_resource.get_parallel_connections()
|
||||
|
||||
# Create CutsceneManager
|
||||
var cutscene_manager = CutsceneManager.new()
|
||||
|
||||
@@ -44,8 +36,8 @@ func generate_cutscene(cutscene_resource: CutsceneResource) -> CutsceneManager:
|
||||
|
||||
# Find the entry node in the graph
|
||||
func _find_entry_node() -> Dictionary:
|
||||
for node_name in graph_nodes:
|
||||
var node_data = graph_nodes[node_name]
|
||||
for node_id in graph_nodes:
|
||||
var node_data = graph_nodes[node_id]
|
||||
if node_data["type"] == "entry":
|
||||
return node_data
|
||||
return {}
|
||||
@@ -55,7 +47,7 @@ func _build_action_sequence(start_node: Dictionary, cutscene_manager: CutsceneMa
|
||||
# Use a queue-based traversal to process nodes in order
|
||||
var node_queue = []
|
||||
var processed_nodes = {}
|
||||
var next_connections = _get_outgoing_connections(start_node["name"])
|
||||
var next_connections = _get_outgoing_connections(start_node["id"])
|
||||
|
||||
# Add initial connections to queue
|
||||
for conn in next_connections:
|
||||
@@ -63,59 +55,31 @@ func _build_action_sequence(start_node: Dictionary, cutscene_manager: CutsceneMa
|
||||
|
||||
# Process nodes in order
|
||||
while not node_queue.is_empty():
|
||||
var current_node_name = node_queue.pop_front()
|
||||
var current_node_id = node_queue.pop_front()
|
||||
|
||||
# Skip if already processed
|
||||
if processed_nodes.has(current_node_name):
|
||||
if processed_nodes.has(current_node_id):
|
||||
continue
|
||||
|
||||
# Mark as processed
|
||||
processed_nodes[current_node_name] = true
|
||||
processed_nodes[current_node_id] = true
|
||||
|
||||
# Get node data
|
||||
var node_data = graph_nodes.get(current_node_name, {})
|
||||
var node_data = graph_nodes.get(current_node_id, {})
|
||||
if node_data.is_empty():
|
||||
continue
|
||||
|
||||
# Handle parallel groups specially
|
||||
if node_data["type"] == "parallel":
|
||||
_handle_parallel_group(node_data, cutscene_manager, node_queue)
|
||||
else:
|
||||
# Create action for regular nodes
|
||||
var action = _create_action_from_node(node_data)
|
||||
if action:
|
||||
cutscene_manager.add_action(action)
|
||||
# Create action for regular nodes
|
||||
var action = _create_action_from_node(node_data)
|
||||
if action:
|
||||
cutscene_manager.add_action(action)
|
||||
|
||||
# Add next nodes to queue
|
||||
var outgoing_connections = _get_outgoing_connections(current_node_name)
|
||||
var outgoing_connections = _get_outgoing_connections(current_node_id)
|
||||
for conn in outgoing_connections:
|
||||
if not processed_nodes.has(conn["to_node"]):
|
||||
node_queue.append(conn["to_node"])
|
||||
|
||||
# Handle parallel group nodes
|
||||
func _handle_parallel_group(parallel_node: Dictionary, cutscene_manager: CutsceneManager, node_queue: Array) -> void:
|
||||
# Find all nodes connected to this parallel group
|
||||
var parallel_actions = []
|
||||
|
||||
# Look for logical connections to this parallel group
|
||||
for conn in logical_connections:
|
||||
if conn["to_node"] == parallel_node["name"] and conn.get("is_parallel", false):
|
||||
var source_node_name = conn["from_node"]
|
||||
var source_node_data = graph_nodes.get(source_node_name, {})
|
||||
if not source_node_data.is_empty():
|
||||
var action = _create_action_from_node(source_node_data)
|
||||
if action:
|
||||
parallel_actions.append(action)
|
||||
|
||||
# Add parallel actions to cutscene manager
|
||||
if not parallel_actions.is_empty():
|
||||
cutscene_manager.add_parallel_actions(parallel_actions)
|
||||
|
||||
# Add nodes connected to parallel group output to queue
|
||||
var outgoing_connections = _get_outgoing_connections(parallel_node["name"])
|
||||
for conn in outgoing_connections:
|
||||
node_queue.append(conn["to_node"])
|
||||
|
||||
# Create an action instance from node data
|
||||
func _create_action_from_node(node_data: Dictionary):
|
||||
var parameters = node_data["parameters"]
|
||||
@@ -172,141 +136,17 @@ func _create_action_from_node(node_data: Dictionary):
|
||||
return null
|
||||
|
||||
# Get outgoing connections from a node
|
||||
func _get_outgoing_connections(node_name: String) -> Array:
|
||||
func _get_outgoing_connections(node_id: String) -> Array:
|
||||
var outgoing = []
|
||||
for conn in connections:
|
||||
if conn["from_node"] == node_name:
|
||||
if conn["from_node"] == node_id:
|
||||
outgoing.append(conn)
|
||||
return outgoing
|
||||
|
||||
# Get incoming connections to a node
|
||||
func _get_incoming_connections(node_name: String) -> Array:
|
||||
func _get_incoming_connections(node_id: String) -> Array:
|
||||
var incoming = []
|
||||
for conn in connections:
|
||||
if conn["to_node"] == node_name:
|
||||
if conn["to_node"] == node_id:
|
||||
incoming.append(conn)
|
||||
return incoming
|
||||
|
||||
# Generate GDScript code from graph data (alternative output format)
|
||||
func generate_gdscript_code(cutscene_resource: CutsceneResource) -> String:
|
||||
var code = "# Generated Cutscene Code\n"
|
||||
code += "extends Node2D\n\n"
|
||||
|
||||
# Add character variables
|
||||
code += "# Character nodes\n"
|
||||
# This would need to be determined from the graph data
|
||||
code += "@onready var character1: Node2D = $Character1\n"
|
||||
code += "@onready var character2: Node2D = $Character2\n\n"
|
||||
|
||||
code += "# Cutscene manager\n"
|
||||
code += "var cutscene_manager: CutsceneManager\n\n"
|
||||
|
||||
code += "func _ready() -> void:\n"
|
||||
code += " # Initialize the cutscene system\n"
|
||||
code += " setup_cutscene()\n"
|
||||
code += " \n"
|
||||
code += " # Start the cutscene\n"
|
||||
code += " cutscene_manager.start()\n\n"
|
||||
|
||||
code += "func setup_cutscene() -> void:\n"
|
||||
code += " # Create the cutscene manager\n"
|
||||
code += " cutscene_manager = CutsceneManager.new()\n"
|
||||
code += " add_child(cutscene_manager)\n"
|
||||
code += " \n"
|
||||
|
||||
# Generate action sequence
|
||||
var action_sequence = _generate_action_sequence_code(cutscene_resource)
|
||||
code += action_sequence
|
||||
|
||||
code += "\n"
|
||||
code += "func _on_cutscene_completed() -> void:\n"
|
||||
code += " print(\"Cutscene completed!\")\n"
|
||||
|
||||
return code
|
||||
|
||||
# Generate action sequence code
|
||||
func _generate_action_sequence_code(cutscene_resource: CutsceneResource) -> String:
|
||||
var code = ""
|
||||
|
||||
# This is a simplified approach - a real implementation would need to
|
||||
# properly traverse the graph and handle parallel groups
|
||||
|
||||
# Find entry node and traverse from there
|
||||
var entry_node = _find_entry_node_in_resource(cutscene_resource)
|
||||
if entry_node.is_empty():
|
||||
return code
|
||||
|
||||
# For demonstration, we'll just generate code for each node in order
|
||||
# A real implementation would need proper graph traversal
|
||||
|
||||
for node_data in cutscene_resource.nodes:
|
||||
if node_data["type"] == "entry" or node_data["type"] == "exit":
|
||||
continue
|
||||
|
||||
var action_code = _generate_action_code(node_data)
|
||||
if not action_code.is_empty():
|
||||
code += " " + action_code + "\n"
|
||||
|
||||
return code
|
||||
|
||||
# Find entry node in resource
|
||||
func _find_entry_node_in_resource(cutscene_resource: CutsceneResource) -> Dictionary:
|
||||
for node_data in cutscene_resource.nodes:
|
||||
if node_data["type"] == "entry":
|
||||
return node_data
|
||||
return {}
|
||||
|
||||
# Generate code for a single action
|
||||
func _generate_action_code(node_data: Dictionary) -> String:
|
||||
var parameters = node_data["parameters"]
|
||||
|
||||
match node_data["type"]:
|
||||
"move":
|
||||
var character = parameters.get("character", "character1")
|
||||
var x = parameters.get("target_x", 0.0)
|
||||
var y = parameters.get("target_y", 0.0)
|
||||
var speed = parameters.get("speed", 100.0)
|
||||
return "cutscene_manager.add_action(MoveAction.new(%s, Vector2(%f, %f), %f))" % [character, x, y, speed]
|
||||
|
||||
"turn":
|
||||
var character = parameters.get("character", "character1")
|
||||
var target = parameters.get("target", "character2")
|
||||
var speed = parameters.get("turn_speed", 2.0)
|
||||
return "cutscene_manager.add_action(TurnAction.new(%s, %s, %f))" % [character, target, speed]
|
||||
|
||||
"dialogue":
|
||||
var character = parameters.get("character", "character1")
|
||||
var text = parameters.get("text", "")
|
||||
var duration = parameters.get("duration", 0.0)
|
||||
# Escape quotes in text
|
||||
var escaped_text = text.replace("\"", "\\\"")
|
||||
return "cutscene_manager.add_action(DialogueAction.new(%s, \"%s\", %f))" % [character, escaped_text, duration]
|
||||
|
||||
"animation":
|
||||
var character = parameters.get("character", "character1")
|
||||
var anim_name = parameters.get("animation_name", "")
|
||||
var loop = "true" if parameters.get("loop", false) else "false"
|
||||
return "cutscene_manager.add_action(AnimationAction.new(%s, \"%s\", %s))" % [character, anim_name, loop]
|
||||
|
||||
"wait":
|
||||
var duration = parameters.get("duration", 1.0)
|
||||
return "cutscene_manager.add_action(WaitAction.new(%f))" % duration
|
||||
|
||||
_:
|
||||
return ""
|
||||
|
||||
# Export to different formats
|
||||
func export_to_format(cutscene_resource: CutsceneResource, format: String) -> String:
|
||||
match format:
|
||||
"gdscript":
|
||||
return generate_gdscript_code(cutscene_resource)
|
||||
"json":
|
||||
return _export_to_json(cutscene_resource)
|
||||
_:
|
||||
return ""
|
||||
|
||||
# Export to JSON format
|
||||
func _export_to_json(cutscene_resource: CutsceneResource) -> String:
|
||||
# Convert the resource to JSON
|
||||
var json = JSON.new()
|
||||
return json.stringify(cutscene_resource, " ")
|
||||
|
||||
@@ -289,7 +289,7 @@ func _on_graph_node_deleted(node: BaseGraphNode) -> void:
|
||||
# Remove all connections to/from this node
|
||||
var connections = get_connection_list()
|
||||
for connection in connections:
|
||||
if connection["from_node"] == node.name or connection["to_node"] == node.name:
|
||||
if connection["from_node"] == node.node_id or connection["to_node"] == node.node_id:
|
||||
disconnect_node(connection["from_node"], connection["from_port"], connection["to_node"], connection["to_port"])
|
||||
|
||||
|
||||
@@ -342,16 +342,16 @@ func load_from_cutscene(cutscene: CutsceneResource) -> void:
|
||||
|
||||
# Create nodes from cutscene data
|
||||
for node_data in cutscene.nodes:
|
||||
var node = add_node(node_data["type"], Vector2(node_data["x"], node_data["y"]))
|
||||
var node = add_node(node_data["type"], Vector2(node_data["position"]["x"], node_data["position"]["y"]))
|
||||
if node:
|
||||
node.name = node_data["name"]
|
||||
node.node_id = node_data["id"]
|
||||
# Set node parameters
|
||||
for param_name in node_data["parameters"]:
|
||||
node.set_parameter(param_name, node_data["parameters"][param_name])
|
||||
|
||||
# Create connections from cutscene data
|
||||
for connection_data in cutscene.connections:
|
||||
connect_node(connection_data["from_node"], connection_data["from_port"],
|
||||
connect_node(connection_data["from_node"], connection_data["from_port"],
|
||||
connection_data["to_node"], connection_data["to_port"])
|
||||
|
||||
# Emit signal
|
||||
@@ -370,10 +370,12 @@ func save_to_cutscene() -> CutsceneResource:
|
||||
for child in get_children():
|
||||
if child is BaseGraphNode:
|
||||
var node_data = {
|
||||
"name": child.name,
|
||||
"id": child.node_id,
|
||||
"type": child.node_type,
|
||||
"x": child.position_offset.x,
|
||||
"y": child.position_offset.y,
|
||||
"position": {
|
||||
"x": child.position_offset.x,
|
||||
"y": child.position_offset.y
|
||||
},
|
||||
"parameters": child.action_parameters
|
||||
}
|
||||
current_cutscene.nodes.append(node_data)
|
||||
@@ -381,6 +383,7 @@ func save_to_cutscene() -> CutsceneResource:
|
||||
# Save connections
|
||||
for connection in get_connection_list():
|
||||
var connection_data = {
|
||||
"id": _generate_unique_connection_id(),
|
||||
"from_node": connection["from_node"],
|
||||
"from_port": connection["from_port"],
|
||||
"to_node": connection["to_node"],
|
||||
@@ -390,6 +393,10 @@ func save_to_cutscene() -> CutsceneResource:
|
||||
|
||||
return current_cutscene
|
||||
|
||||
# Generate a unique ID for connections
|
||||
func _generate_unique_connection_id() -> String:
|
||||
return "conn_" + str(Time.get_ticks_msec())
|
||||
|
||||
|
||||
# Set up preview system
|
||||
func setup_preview() -> void:
|
||||
|
||||
@@ -7,30 +7,31 @@ extends Resource
|
||||
# Properties
|
||||
@export var nodes: Array = [] # List of node data
|
||||
@export var connections: Array = [] # List of connection data
|
||||
@export var parallel_connections: Array = [] # Logical connections for parallel groups
|
||||
@export var metadata: Dictionary = {} # Additional metadata
|
||||
|
||||
# Initialize the resource
|
||||
func _init() -> void:
|
||||
nodes = []
|
||||
connections = []
|
||||
parallel_connections = []
|
||||
metadata = {
|
||||
"version": "1.0",
|
||||
"version": "2.0",
|
||||
"created": Time.get_unix_time_from_system(),
|
||||
"modified": Time.get_unix_time_from_system()
|
||||
}
|
||||
|
||||
# Add a node to the cutscene
|
||||
func add_node(node_data: Dictionary) -> void:
|
||||
# Generate unique ID if not provided
|
||||
if not node_data.has("id") or node_data["id"] == "":
|
||||
node_data["id"] = _generate_unique_id()
|
||||
nodes.append(node_data)
|
||||
metadata["modified"] = Time.get_unix_time_from_system()
|
||||
|
||||
# Remove a node from the cutscene
|
||||
func remove_node(node_name: String) -> void:
|
||||
func remove_node(node_id: String) -> void:
|
||||
# Remove the node
|
||||
for i in range(nodes.size()):
|
||||
if nodes[i].has("name") and nodes[i]["name"] == node_name:
|
||||
if nodes[i].has("id") and nodes[i]["id"] == node_id:
|
||||
nodes.remove_at(i)
|
||||
break
|
||||
|
||||
@@ -38,58 +39,36 @@ func remove_node(node_name: String) -> void:
|
||||
var i = 0
|
||||
while i < connections.size():
|
||||
var conn = connections[i]
|
||||
if conn["from_node"] == node_name or conn["to_node"] == node_name:
|
||||
if conn["from_node"] == node_id or conn["to_node"] == node_id:
|
||||
connections.remove_at(i)
|
||||
else:
|
||||
i += 1
|
||||
|
||||
# Remove any parallel connections to/from this node
|
||||
i = 0
|
||||
while i < parallel_connections.size():
|
||||
var conn = parallel_connections[i]
|
||||
if conn["from_node"] == node_name or conn["to_node"] == node_name:
|
||||
parallel_connections.remove_at(i)
|
||||
else:
|
||||
i += 1
|
||||
|
||||
metadata["modified"] = Time.get_unix_time_from_system()
|
||||
|
||||
# Add a connection to the cutscene
|
||||
func add_connection(connection_data: Dictionary) -> void:
|
||||
# Generate unique ID if not provided
|
||||
if not connection_data.has("id") or connection_data["id"] == "":
|
||||
connection_data["id"] = _generate_unique_id()
|
||||
connections.append(connection_data)
|
||||
metadata["modified"] = Time.get_unix_time_from_system()
|
||||
|
||||
# Remove a connection from the cutscene
|
||||
func remove_connection(from_node: String, from_port: int, to_node: String, to_port: int) -> void:
|
||||
func remove_connection(connection_id: String) -> void:
|
||||
for i in range(connections.size()):
|
||||
var conn = 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):
|
||||
if conn["id"] == connection_id:
|
||||
connections.remove_at(i)
|
||||
break
|
||||
|
||||
metadata["modified"] = Time.get_unix_time_from_system()
|
||||
|
||||
# Add a parallel connection to the cutscene
|
||||
func add_parallel_connection(connection_data: Dictionary) -> void:
|
||||
parallel_connections.append(connection_data)
|
||||
metadata["modified"] = Time.get_unix_time_from_system()
|
||||
|
||||
# Remove a parallel connection from the cutscene
|
||||
func remove_parallel_connection(from_node: String, from_port: int, to_node: String, to_port: int) -> void:
|
||||
for i in range(parallel_connections.size()):
|
||||
var conn = parallel_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):
|
||||
parallel_connections.remove_at(i)
|
||||
break
|
||||
|
||||
metadata["modified"] = Time.get_unix_time_from_system()
|
||||
|
||||
# Get node by name
|
||||
func get_node_by_name(node_name: String) -> Dictionary:
|
||||
# Get node by id
|
||||
func get_node_by_id(node_id: String) -> Dictionary:
|
||||
for node in nodes:
|
||||
if node.has("name") and node["name"] == node_name:
|
||||
if node.has("id") and node["id"] == node_id:
|
||||
return node
|
||||
return {}
|
||||
|
||||
@@ -101,21 +80,25 @@ func get_connections_for_node(node_name: String) -> Array:
|
||||
node_connections.append(conn)
|
||||
return node_connections
|
||||
|
||||
# Get all parallel connections for a node
|
||||
func get_parallel_connections_for_node(node_name: String) -> Array:
|
||||
var node_connections = []
|
||||
for conn in parallel_connections:
|
||||
if conn["from_node"] == node_name or conn["to_node"] == node_name:
|
||||
node_connections.append(conn)
|
||||
return node_connections
|
||||
|
||||
# Validate that all referenced nodes exist in the resource
|
||||
func validate() -> bool:
|
||||
# Check if all nodes referenced in connections exist
|
||||
for conn in connections:
|
||||
if not get_node_by_id(conn["from_node"]) or not get_node_by_id(conn["to_node"]):
|
||||
return false
|
||||
return true
|
||||
|
||||
# Clear all data
|
||||
func clear() -> void:
|
||||
nodes.clear()
|
||||
connections.clear()
|
||||
parallel_connections.clear()
|
||||
metadata["modified"] = Time.get_unix_time_from_system()
|
||||
|
||||
# Update metadata
|
||||
func update_metadata() -> void:
|
||||
metadata["modified"] = Time.get_unix_time_from_system()
|
||||
|
||||
# Generate a unique ID for nodes and connections
|
||||
func _generate_unique_id() -> String:
|
||||
return "id_" + str(Time.get_ticks_msec())
|
||||
|
||||
Reference in New Issue
Block a user