initial
This commit is contained in:
313
action_types_design.md
Normal file
313
action_types_design.md
Normal file
@@ -0,0 +1,313 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user