Files
experiment-adventure-ai/cutscene/CutsceneManager.gd
2025-08-01 08:34:51 -07:00

194 lines
5.1 KiB
GDScript
Executable File

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()
func _on_action_started(action: Action) -> void:
# Handle action started
emit_signal("action_started", action)
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)
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