extends Node2D # Dialogue system for Godot 4.x # Handles displaying dialogue with animations and text reveal # Signals signal dialogue_started() signal dialogue_completed() # Configuration @export var text_reveal_speed: float = 0.05 # seconds per character @export var show_animation_duration: float = 0.3 # seconds @export var hide_animation_duration: float = 0.2 # seconds # Private variables var dialogue_box: Control = null var text_label: Label = null var timer: Timer = null var current_text: String = "" var current_duration: float = 0.0 var text_index: int = 0 var is_showing: bool = false var is_revealing: bool = false func _ready(): # Initialize the dialogue system _create_dialogue_ui() func _create_dialogue_ui(): # Create dialogue box UI dialogue_box = Control.new() dialogue_box.name = "DialogueBox" # Create background var background = ColorRect.new() background.name = "Background" background.color = Color(1, 1, 1, 1) # White background background.size = Vector2(300, 80) background.anchor_left = 0.5 background.anchor_top = 1.0 background.anchor_right = 0.5 background.anchor_bottom = 1.0 background.position = Vector2(-150, -40) # Positioned above character background.pivot_offset = Vector2(150, 40) # Center pivot # Create text label text_label = Label.new() text_label.name = "TextLabel" text_label.text = "" text_label.size = Vector2(280, 60) text_label.anchor_left = 0.5 text_label.add_theme_color_override("font_color", Color.BLACK) text_label.anchor_top = 0.5 text_label.anchor_right = 0.5 text_label.anchor_bottom = 0.5 text_label.position = Vector2(-140, -30) # Centered in background text_label.pivot_offset = Vector2(140, 30) # Center pivot text_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT text_label.vertical_alignment = VERTICAL_ALIGNMENT_TOP text_label.autowrap_mode = 2 # TextServer.AUTOWRAP_WORD_SMART #text_label.custom_styles.normal.font_size = 16 # Add to dialogue box dialogue_box.add_child(background) dialogue_box.add_child(text_label) # Set initial state - hidden and transparent dialogue_box.visible = false dialogue_box.modulate.a = 0.0 # Add to scene tree (should be added to parent character) add_child(dialogue_box) # Position the dialogue box above the character # This ensures it's positioned correctly relative to the character if get_parent() != null: var parent_pos = get_parent().position dialogue_box.position = Vector2(parent_pos.x, parent_pos.y - 50) # Position above character func trigger_dialogue(text: String, duration: float = 3.0) -> void: """Trigger dialogue with specified text and duration""" if is_showing: return current_text = text current_duration = duration text_index = 0 is_showing = true is_revealing = false # Show the dialogue box with animation show_dialogue_box() # Start revealing text after a short delay to allow animation to complete await get_tree().create_timer(show_animation_duration) # Start revealing text immediately start_text_reveal() func show_dialogue_box(): """Show dialogue box with animation""" dialogue_box.visible = true # Animate in using Tween var tween = create_tween() tween.set_ease(Tween.EASE_OUT) tween.set_trans(Tween.TRANS_SINE) tween.tween_property(dialogue_box, "modulate:a", 1.0, show_animation_duration) # Emit signal when animation completes await tween.finished emit_signal("dialogue_started") func hide_dialogue_box(): """Hide dialogue box with animation""" # Animate out using Tween var tween = create_tween() tween.set_ease(Tween.EASE_IN) tween.set_trans(Tween.TRANS_SINE) tween.tween_property(dialogue_box, "modulate:a", 0.0, hide_animation_duration) # Wait for animation to complete await tween.finished dialogue_box.visible = false is_showing = false is_revealing = false emit_signal("dialogue_completed") func start_text_reveal(): """Start revealing text character by character""" if is_revealing: return is_revealing = true text_label.text = "" text_index = 0 # If duration is 0 or negative, don't auto-hide if current_duration <= 0: # Just reveal all text immediately text_label.text = current_text is_revealing = false return # Start timer for automatic hiding timer = Timer.new() timer.wait_time = current_duration timer.one_shot = true timer.connect("timeout", self._on_dialogue_timeout) add_child(timer) timer.start() # Start the character-by-character reveal reveal_next_character() func reveal_next_character(): """Reveal the next character in the text""" if text_index >= current_text.length(): # Finished revealing all characters is_revealing = false return # Add the next character to the text label text_label.text += current_text[text_index] text_index += 1 # Schedule next character reveal await get_tree().create_timer(text_reveal_speed) reveal_next_character() func _on_dialogue_timeout(): """Handle dialogue timeout""" if is_showing and not is_revealing: hide_dialogue_box()