# Cutscene System Extensibility Guide ## Overview This document explains how to extend the cutscene system with new action types, features, and customizations while maintaining compatibility with the existing architecture. ## Adding New Action Types ### Basic Extension Process 1. Create a new class that extends `Action` 2. Implement the required methods (`start()`, `is_completed()`) 3. Optionally implement `update()` and `stop()` 4. Add any custom properties or methods needed ### Example: Custom FadeAction ```gdscript class_name FadeAction extends Action # Custom properties var target_node: CanvasItem var target_alpha: float var fade_speed: float = 1.0 var start_alpha: float = 1.0 func _init(node: CanvasItem, alpha: float, speed: float = 1.0): target_node = node target_alpha = alpha fade_speed = speed name = "FadeAction" func start() -> void: if target_node == null: ._set_failed("Target node is null") return start_alpha = target_node.modulate.a ._set_running() func update(delta: float) -> void: if state != State.RUNNING: return if target_node == null: ._set_failed("Target node was destroyed") return # Calculate new alpha value var current_alpha = target_node.modulate.a var alpha_diff = target_alpha - current_alpha var alpha_change = sign(alpha_diff) * fade_speed * delta # Check if we've reached the target if abs(alpha_change) >= abs(alpha_diff): # Set final alpha and complete var new_modulate = target_node.modulate new_modulate.a = target_alpha target_node.modulate = new_modulate ._set_completed() else: # Apply incremental change var new_modulate = target_node.modulate new_modulate.a += alpha_change target_node.modulate = new_modulate func is_completed() -> bool: return state == State.COMPLETED ``` ## Customizing the CutsceneManager ### Adding New Features The CutsceneManager can be extended with new functionality: ```gdscript # Extended CutsceneManager with additional features class_name ExtendedCutsceneManager extends CutsceneManager # Custom properties var skip_enabled: bool = true var auto_skip_delay: float = 0.0 # Custom signals signal cutscene_skipped() # Custom methods func skip_cutscene() -> void: if not skip_enabled: return emit_signal("cutscene_skipped") stop() func set_auto_skip(seconds: float) -> void: auto_skip_delay = seconds # Implementation for auto-skip functionality ``` ### Plugin Architecture For complex extensions, consider a plugin-style architecture: ```gdscript # Plugin interface class_name CutscenePlugin extends RefCounted func initialize(manager: CutsceneManager) -> void: # Initialize plugin with the manager pass func process_action(action: Action, delta: float) -> void: # Process action each frame pass func cleanup() -> void: # Clean up when cutscene ends pass # Example plugin for debugging class_name DebugCutscenePlugin extends CutscenePlugin func process_action(action: Action, delta: float) -> void: if action.state == Action.State.RUNNING: print("Action %s is running" % action.name) ``` ## Integration with External Systems ### Game State Integration Connect the cutscene system to your game's state management: ```gdscript # Integration with a game state manager class_name GameStateIntegratedCutsceneManager extends CutsceneManager var game_state_manager: GameStateManager func start() -> void: # Notify game state manager if game_state_manager: game_state_manager.set_state(GameState.CUTSCENE) # Call parent start method super().start() func _on_cutscene_completed() -> void: # Notify game state manager if game_state_manager: game_state_manager.set_state(GameState.PLAYING) # Call parent completion handler super()._on_cutscene_completed() ``` ### Save/Load System Integration Add support for saving and loading cutscene states: ```gdscript class_name SaveableCutsceneManager extends CutsceneManager func save_state() -> Dictionary: return { "current_action_index": action_index, "sequential_actions": _serialize_actions(sequential_actions), "parallel_groups": _serialize_parallel_groups(active_parallel_groups), "state": state } func load_state(data: Dictionary) -> void: action_index = data["current_action_index"] sequential_actions = _deserialize_actions(data["sequential_actions"]) active_parallel_groups = _deserialize_parallel_groups(data["parallel_groups"]) state = data["state"] ``` ## Performance Optimization Extensions ### Action Pooling Implement object pooling for frequently created actions: ```gdscript class_name PooledCutsceneManager extends CutsceneManager var action_pools: Dictionary = {} func get_pooled_action(action_type: String, params: Array) -> Action: if not action_pools.has(action_type): action_pools[action_type] = [] var pool = action_pools[action_type] if pool.size() > 0: # Reuse existing action var action = pool.pop_back() # Reinitialize with new parameters action.reinitialize(params) return action else: # Create new action return _create_action(action_type, params) func return_action_to_pool(action: Action) -> void: var action_type = action.get_class() if not action_pools.has(action_type): action_pools[action_type] = [] action.reset() action_pools[action_type].append(action) ``` ## Custom Action Composition ### Action Groups Create reusable action groups for common sequences: ```gdscript class_name ActionGroup extends Action var actions: Array var current_action_index: int = 0 func _init(group_actions: Array): actions = group_actions name = "ActionGroup" func start() -> void: current_action_index = 0 if actions.size() > 0: _execute_action(actions[0]) else: ._set_completed() func _execute_action(action: Action) -> void: action.connect("completed", _on_sub_action_completed.bind(action)) action.start() func _on_sub_action_completed(action: Action) -> void: current_action_index += 1 if current_action_index < actions.size(): _execute_action(actions[current_action_index]) else: ._set_completed() ``` ## Event-Driven Extensions ### Custom Events Add support for custom events that can trigger actions: ```gdscript class_name EventDrivenCutsceneManager extends CutsceneManager var event_listeners: Dictionary = {} func listen_for_event(event_name: String, action: Action) -> void: if not event_listeners.has(event_name): event_listeners[event_name] = [] event_listeners[event_name].append(action) func trigger_event(event_name: String) -> void: if event_listeners.has(event_name): for action in event_listeners[event_name]: # Add action to current sequence or execute immediately add_action(action) ``` ## Best Practices for Extensions ### 1. Maintain Compatibility - Always call parent methods when overriding - Keep the same method signatures - Don't change the core behavior of existing methods ### 2. Use Composition Over Inheritance - Prefer adding functionality through plugins or components - Keep inheritance hierarchies shallow - Use interfaces where possible ### 3. Provide Clear Extension Points - Document which methods are safe to override - Provide virtual methods for customization - Use signals for loose coupling ### 4. Handle Errors Gracefully - Always check for null references - Provide meaningful error messages - Implement fallback behaviors ### 5. Optimize for Performance - Reuse objects when possible - Avoid unnecessary processing - Profile extensions for performance impact This extensibility guide provides a framework for expanding the cutscene system while maintaining its core functionality and ease of use.