# Action Types Design ## Overview This document details the specific action types that will be implemented for the cutscene system. Each action type handles a specific kind of operation in a cutscene. ## Core Action Types ### 1. MoveAction Moves a character to a specific position over time. ```gdscript class_name MoveAction extends Action # Properties var character: Node2D # The character to move var target_position: Vector2 # Target position var speed: float = 100.0 # Movement speed (pixels/second) var threshold: float = 5.0 # Distance threshold for completion # Internal var start_position: Vector2 var distance: float var traveled: float = 0.0 func _init(character_node: Node2D, target: Vector2, move_speed: float = 100.0): character = character_node target_position = target speed = move_speed name = "MoveAction" func start() -> void: if character == null: ._set_failed("Character is null") return start_position = character.position distance = start_position.distance_to(target_position) traveled = 0.0 ._set_running() func update(delta: float) -> void: if state != State.RUNNING: return if character == null: ._set_failed("Character was destroyed during action") return # Calculate movement for this frame var frame_distance = speed * delta traveled += frame_distance # If we've reached or overshot the target if traveled >= distance: character.position = target_position ._set_completed() return # Move character along the path var direction = (target_position - start_position).normalized() character.position = start_position + direction * traveled func is_completed() -> bool: return state == State.COMPLETED ``` ### 2. TurnAction Makes a character turn to face a direction or another character. ```gdscript class_name TurnAction extends Action # Properties var character: Node2D # The character to turn var target: Variant # Either a Vector2 position or another Node2D var turn_speed: float = 2.0 # Rotation speed (radians/second) func _init(character_node: Node2D, turn_target: Variant, speed: float = 2.0): character = character_node target = turn_target turn_speed = speed name = "TurnAction" func start() -> void: if character == null: ._set_failed("Character is null") return ._set_running() func update(delta: float) -> void: if state != State.RUNNING: return if character == null: ._set_failed("Character was destroyed during action") return # Calculate target rotation var target_position: Vector2 if target is Vector2: target_position = target elif target is Node2D: target_position = target.position else: ._set_failed("Invalid target type") return var direction = target_position - character.position var target_rotation = atan2(direction.y, direction.x) # Rotate toward target var angle_diff = angle_difference(character.rotation, target_rotation) # If we're close enough, complete the action if abs(angle_diff) < 0.01: character.rotation = target_rotation ._set_completed() return # Rotate based on turn speed var rotation_amount = sign(angle_diff) * turn_speed * delta if abs(rotation_amount) > abs(angle_diff): character.rotation = target_rotation ._set_completed() else: character.rotation += rotation_amount func is_completed() -> bool: return state == State.COMPLETED ``` ### 3. DialogueAction Displays dialogue text, potentially with character-specific formatting. ```gdscript class_name DialogueAction extends Action # Properties var character: Node2D # The speaking character (optional) var text: String # Dialogue text var duration: float = 0.0 # Duration to display (0 = manual advance) var auto_advance: bool = true # Whether to auto-advance after duration func _init(speaking_character: Node2D, dialogue_text: String, display_duration: float = 0.0): character = speaking_character text = dialogue_text duration = display_duration name = "DialogueAction" func start() -> void: # In a real implementation, this would interface with a dialogue system # For now, we'll simulate by printing to console print("%s: %s" % [character.name if character else "Narrator", text]) ._set_running() # If duration is 0, this is a manual advance action # In a real game, this would wait for player input if duration <= 0: # For demo purposes, we'll complete immediately # In a real game, this would wait for input ._set_completed() else: # Start a timer for automatic completion var timer = Timer.new() timer.wait_time = duration timer.one_shot = true timer.connect("timeout", _on_timer_timeout) # In a real implementation, we'd add this to the scene tree # add_child(timer) timer.start() func _on_timer_timeout(): ._set_completed() func is_completed() -> bool: return state == State.COMPLETED ``` ### 4. AnimationAction Plays a specific animation on a character. ```gdscript class_name AnimationAction extends Action # Properties var character: Node2D # The character to animate var animation_name: String # Name of the animation to play var loop: bool = false # Whether to loop the animation func _init(character_node: Node2D, anim_name: String, should_loop: bool = false): character = character_node animation_name = anim_name loop = should_loop name = "AnimationAction" func start() -> void: if character == null: ._set_failed("Character is null") return # In a real implementation, this would interface with an AnimationPlayer # For now, we'll simulate by printing to console print("Playing animation '%s' on %s" % [animation_name, character.name]) # Check if character has an AnimationPlayer var anim_player = _get_animation_player() if anim_player == null: ._set_failed("Character has no AnimationPlayer") return # Connect to animation finished signal for non-looping animations if not loop: if not anim_player.is_connected("animation_finished", _on_animation_finished): anim_player.connect("animation_finished", _on_animation_finished) # Play the animation anim_player.play(animation_name, loop) ._set_running() # For looping animations, we complete immediately # (they would be stopped by another action) if loop: ._set_completed() func _get_animation_player() -> AnimationPlayer: # Try to find AnimationPlayer as a child for child in character.get_children(): if child is AnimationPlayer: return child # Try to find AnimationPlayer in the scene tree return character.get_node_or_null("AnimationPlayer") as AnimationPlayer func _on_animation_finished(anim_name: String): if anim_name == animation_name: ._set_completed() func is_completed() -> bool: return state == State.COMPLETED ``` ### 5. WaitAction Pauses execution for a specified time. ```gdscript class_name WaitAction extends Action # Properties var duration: float # Time to wait in seconds var elapsed_time: float = 0.0 func _init(wait_duration: float): duration = wait_duration name = "WaitAction" func start() -> void: elapsed_time = 0.0 ._set_running() func update(delta: float) -> void: if state != State.RUNNING: return elapsed_time += delta if elapsed_time >= duration: ._set_completed() func is_completed() -> bool: return state == State.COMPLETED ``` ## Extensibility To create new action types: 1. Create a new class that extends `Action` 2. Implement the required methods: - `start()` - Initialize and begin the action - `update(delta)` - Update the action each frame (if needed) - `is_completed()` - Return true when the action is finished 3. Optionally implement `stop()` for cleanup 4. Use appropriate signals to notify completion Example of a custom action: ```gdscript class_name CustomAction extends Action # Custom properties for this action var custom_parameter: String func _init(param: String): custom_parameter = param name = "CustomAction" func start() -> void: # Perform the action print("Executing custom action with parameter: %s" % custom_parameter) # For immediate actions, complete right away ._set_completed() func is_completed() -> bool: return state == State.COMPLETED ``` This design provides a solid set of core action types while maintaining the flexibility to add new types as needed.