@tool class_name Npc2D extends AnimatedSprite2D signal interacted @export var enabled := true: set(val): enabled = val if is_node_ready(): _check_sign_mark_enable() @export var height := 60.0: set(val): height = val if is_node_ready(): speaking_sign.position.y = -height @onready var speaking_animation = %SpeakingAnimationPlayer @onready var speaking_sign = %SpeakingSign2D as Node2D @onready var sign_mark = %Sign as Sign @onready var area2d = %Area2D as Area2D var dialogue_title := "" var dialogue_res = preload("res://asset/dialogue/npc.dialogue") var base_scale := Vector2.ONE var base_mod := Color.WHITE_SMOKE func _ready() -> void: speaking_sign.position.y = -height if Engine.is_editor_hint(): # editor 直接展示 speaking_sign.visible = true speaking_sign.modulate.a = 1.0 return # 默认为 0 speaking_sign.modulate.a = 0.0 base_scale = speaking_sign.scale base_mod = speaking_sign.modulate area2d.body_entered.connect(_on_entered) area2d.body_exited.connect(_on_exited) sign_mark.interacted.connect(_on_interacted) # sign_mark.cancel.connect(_stop_speaking) sign_mark.toggle_active.connect(_on_toggle_active) _check_sign_mark_enable() visibility_changed.connect(_on_visibility_changed) if sprite_frames and animation: play() func _on_visibility_changed() -> void: _check_sign_mark_enable() func _check_sign_mark_enable(): speaking_sign.visible = enabled sign_mark.enabled = enabled and visible func _on_entered(_body = null) -> void: var tween = create_tween() tween.tween_property(speaking_sign, "modulate:a", 1.0, 0.5) func _on_exited(_body = null) -> void: var tween = create_tween() tween.tween_property(speaking_sign, "modulate:a", 0.0, 0.5) func _on_toggle_active(activated: bool) -> void: if activated: _play_speaking() else: _stop_speaking() var speaking := false func _on_interacted() -> void: if speaking: return # play dialogue if dialogue_title: interacted.emit() DialogueManager.show_dialogue_balloon(dialogue_res, dialogue_title) speaking = true SceneManager.freeze_player(0) DialogueManager.dialogue_ended.connect(_dialog_end, CONNECT_ONE_SHOT) var tween = create_tween() tween.tween_property(speaking_sign, "modulate", Color.WHITE, 0.5) tween.parallel().tween_property(speaking_sign, "scale", base_scale * 1.3, 0.3) func _dialog_end(_res): speaking = false SceneManager.release_player() var tween = create_tween() tween.tween_property(speaking_sign, "modulate", base_mod, 0.5) tween.parallel().tween_property(speaking_sign, "scale", base_scale, 0.3) func _play_speaking() -> void: speaking_animation.play("speaking") func _stop_speaking() -> void: speaking_animation.play("RESET") func _get(property: StringName) -> Variant: if property == "dialogue_title": return dialogue_title return null func _set(property: StringName, value: Variant) -> bool: if property == "dialogue_title": dialogue_title = value return true return false func _get_property_list() -> Array[Dictionary]: var hint_str = "" if Engine.is_editor_hint(): hint_str = ",".join(dialogue_res.get_ordered_titles()) return [ { "name": "dialogue_title", "type": TYPE_STRING, "hint": PROPERTY_HINT_ENUM_SUGGESTION, "hint_string": hint_str } ]