class_name CutsceneManager extends Node # Action dependency graph var actions: Dictionary = {} # Map of action IDs to action instances var dependencies: Dictionary = {} # Map of action IDs to their dependency lists var dependents: Dictionary = {} # Map of action IDs to actions that depend on them var ready_actions: Array = [] # Actions that are ready to execute (all dependencies met) var active_actions: Array = [] # Currently running actions var completed_actions: Array = [] # Actions that have completed # State management enum State { IDLE, # Not running any cutscene RUNNING, # Currently executing a cutscene PAUSED # Cutscene is paused } var state: int = State.IDLE var is_active: bool = false # Signals signal cutscene_started() signal cutscene_completed() signal cutscene_paused() signal cutscene_resumed() signal action_started(action) signal action_completed(action) signal action_failed(action, error_message) # Public methods func start() -> void: # Start executing the cutscene if state != State.IDLE: return state = State.RUNNING is_active = true emit_signal("cutscene_started") # Find actions with no dependencies to start with _find_ready_actions() # Start all ready actions for action_id in ready_actions: _execute_action(action_id) # Check if there are no actions to execute if actions.size() == 0: _on_cutscene_completed() func pause() -> void: # Pause the current cutscene if state == State.RUNNING: state = State.PAUSED emit_signal("cutscene_paused") func resume() -> void: # Resume a paused cutscene if state == State.PAUSED: state = State.RUNNING emit_signal("cutscene_resumed") func stop() -> void: # Stop the current cutscene and reset # Stop all active actions for action in active_actions: if action.state == action.State.RUNNING: action.stop() active_actions.clear() _reset() func add_action(action_id: String, action: Action, deps: Array = []) -> void: # Add an action with its dependencies if state != State.IDLE: print("Warning: Cannot add actions while cutscene is running") return actions[action_id] = action dependencies[action_id] = deps # Build reverse dependency map (dependents) for dep_id in deps: if not dependents.has(dep_id): dependents[dep_id] = [] dependents[dep_id].append(action_id) # Internal methods func _process(delta: float) -> void: # Main update loop if state != State.RUNNING: return # Find actions with no dependencies to start with _find_ready_actions() # Start all ready actions for action_id in ready_actions: _execute_action(action_id) # Update all active actions for action in active_actions: if action.state == action.State.RUNNING: action.update(delta) func _find_ready_actions() -> void: # Find actions that are ready to execute (all dependencies met) ready_actions.clear() for action_id in actions: # Skip if already completed or active if completed_actions.has(action_id) or active_actions.has(actions[action_id]): continue # Check if all dependencies are met var all_deps_met = true for dep_id in dependencies[action_id]: if not completed_actions.has(dep_id): all_deps_met = false break if all_deps_met: ready_actions.append(action_id) func _execute_action(action_id: String) -> void: # Execute a single action var action = actions[action_id] # Remove from ready actions ready_actions.erase(action_id) # Add to active actions active_actions.append(action) # Connect to action signals if not action.is_connected("completed", _on_action_completed): action.connect("completed", _on_action_completed.bind(action_id)) if not action.is_connected("failed", _on_action_failed): action.connect("failed", _on_action_failed.bind(action)) if not action.is_connected("started", _on_action_started): action.connect("started", _on_action_started.bind(action)) # Start the action emit_signal("action_started", action) action.start() _debug_state() func _on_action_started(action: Action) -> void: # Handle action started emit_signal("action_started", action) _debug_state() func _debug_state() -> void: # Print debug information about all actions and their dependencies print("=== Cutscene Manager Debug State ===") print("State: %s" % ["IDLE", "RUNNING", "PAUSED"][state]) print("Actions count: %d" % actions.size()) print("Ready actions: %d" % ready_actions.size()) print("Active actions: %d" % active_actions.size()) print("Completed actions: %d" % completed_actions.size()) for action_id in actions: var action = actions[action_id] var action_state = ["PENDING", "RUNNING", "COMPLETED", "FAILED"][action.state] # Get dependencies for this action var deps = dependencies.get(action_id, []) var deps_status = [] for dep_id in deps: if completed_actions.has(dep_id): deps_status.append("%s:COMPLETED" % dep_id) elif active_actions.has(actions[dep_id]): # Check if the dependency action is actually running deps_status.append("%s:RUNNING" % dep_id) else: deps_status.append("%s:PENDING" % dep_id) var deps_str = ", ".join(deps_status) if deps_status.size() > 0 else "None" print("Action '%s': %s (deps: %s)" % [action_id, action_state, deps_str]) print("====================================") func _on_action_completed(action_id: String) -> void: # Handle action completion var action = actions[action_id] active_actions.erase(action) completed_actions.append(action_id) emit_signal("action_completed", action) # Check if all actions are completed if completed_actions.size() == actions.size(): _on_cutscene_completed() else: # Find newly ready actions _find_ready_actions() # Start all newly ready actions for new_action_id in ready_actions: _execute_action(new_action_id) _debug_state() func _on_action_failed(action: Action, error_message: String) -> void: # Handle action failure active_actions.erase(action) emit_signal("action_failed", action, error_message) print("Action failed: %s - %s" % [action.name, error_message]) # Stop the cutscene stop() func _on_cutscene_completed() -> void: # Handle cutscene completion state = State.IDLE is_active = false emit_signal("cutscene_completed") func _reset() -> void: # Reset the manager to initial state actions.clear() dependencies.clear() dependents.clear() ready_actions.clear() active_actions.clear() completed_actions.clear() state = State.IDLE is_active = false