This commit is contained in:
2025-07-30 21:52:27 -07:00
commit a429a3e06b
29 changed files with 3068 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
class_name AnimationAction
extends "res://cutscene/Action.gd"
# 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) -> void:
character = character_node
animation_name = anim_name
loop = should_loop
name = "AnimationAction"
func start() -> void:
if character == null:
self._set_failed("Character is null")
return
# Check if character has an AnimationPlayer
var anim_player = _get_animation_player()
if anim_player == null:
self._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.bind(anim_player))
# Play the animation
anim_player.play(animation_name)
self._set_running()
# For looping animations, we complete immediately
# (they would be stopped by another action)
if loop:
self._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, anim_player: AnimationPlayer) -> void:
# Make sure this is for our animation
if anim_name == animation_name and not loop:
self._set_completed()
func is_completed() -> bool:
return state == State.COMPLETED

View File

@@ -0,0 +1,75 @@
class_name DialogueAction
extends "res://cutscene/Action.gd"
# Properties
var character: Node2D # The speaking character (optional)
var text: String # Dialogue text
var duration: float = 0.0 # Duration to display (0 = manual advance)
func _init(speaking_character: Node2D, dialogue_text: String, display_duration: float = 0.0) -> void:
character = speaking_character
text = dialogue_text
duration = display_duration
name = "DialogueAction"
func start() -> void:
# Display the dialogue text
_display_dialogue()
self._set_running()
# If duration is 0, this is a manual advance action
if duration <= 0:
# For demo purposes, we'll complete immediately
# In a real game, this would wait for player input
self._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)
# Add timer as a child to ensure it processes
if Engine.get_main_loop().current_scene:
Engine.get_main_loop().current_scene.add_child(timer)
#else:
## Fallback if we can't access the main scene
#var root = get_tree().root if get_tree() else null
#if root and root.get_child_count() > 0:
#root.get_child(0).add_child(timer)
#else:
## Last resort - connect directly and manage manually
#timer.connect("timeout", _on_timer_timeout_direct)
#timer.start(duration)
#return
timer.connect("timeout", _cleanup_timer.bind(timer))
timer.start()
func _display_dialogue() -> void:
# In a real implementation, this would interface with a dialogue system
# For now, we'll simulate by printing to console
var character_name = character.name if character and character.name else "Unknown"
print("%s: %s" % [character_name, text])
func _on_timer_timeout() -> void:
self._set_completed()
func _on_timer_timeout_direct() -> void:
# Direct timeout handler for when we can't add the timer to the scene tree
self._set_completed()
func _cleanup_timer(timer: Timer) -> void:
# Clean up the timer
if timer and timer.get_parent():
timer.get_parent().remove_child(timer)
if timer:
timer.queue_free()
self._set_completed()
func is_completed() -> bool:
return state == State.COMPLETED
func stop() -> void:
# Clean up any timers when stopping
super.stop()

View File

@@ -0,0 +1,54 @@
class_name MoveAction
extends "res://cutscene/Action.gd"
# 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) -> void:
character = character_node
target_position = target
speed = move_speed
name = "MoveAction"
func start() -> void:
if character == null:
self._set_failed("Character is null")
return
start_position = character.position
distance = start_position.distance_to(target_position)
traveled = 0.0
self._set_running()
func update(delta: float) -> void:
if state != State.RUNNING:
return
if character == null:
self._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
self._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

View File

@@ -0,0 +1,69 @@
class_name TurnAction
extends "res://cutscene/Action.gd"
# 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) -> void:
character = character_node
target = turn_target
turn_speed = speed
name = "TurnAction"
func start() -> void:
if character == null:
self._set_failed("Character is null")
return
self._set_running()
func update(delta: float) -> void:
if state != State.RUNNING:
return
if character == null:
self._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:
self._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
self._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
self._set_completed()
else:
character.rotation += rotation_amount
func is_completed() -> bool:
return state == State.COMPLETED
# Helper function to calculate angle difference
func _angle_difference(from: float, to: float) -> float:
var diff = fmod(to - from, 2.0 * PI)
if diff < -PI:
diff += 2.0 * PI
elif diff > PI:
diff -= 2.0 * PI
return diff

View File

@@ -0,0 +1,25 @@
class_name WaitAction
extends "res://cutscene/Action.gd"
# Properties
var duration: float # Time to wait in seconds
var elapsed_time: float = 0.0
func _init(wait_duration: float) -> void:
duration = wait_duration
name = "WaitAction"
func start() -> void:
elapsed_time = 0.0
self._set_running()
func update(delta: float) -> void:
if state != State.RUNNING:
return
elapsed_time += delta
if elapsed_time >= duration:
self._set_completed()
func is_completed() -> bool:
return state == State.COMPLETED