Files
experiment-adventure-ai/extensibility_guide.md
2025-07-30 21:52:27 -07:00

7.9 KiB

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

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:

# 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:

# 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:

# 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:

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:

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:

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:

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.