231 lines
6.4 KiB
GDScript
Executable File
231 lines
6.4 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()
|
|
|
|
_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
|