@tool class_name Npc2D extends AnimatedSprite2D signal interacted @export var anonymous_title_suffix := "_匿名" @export var character_name := "" @export var anonymous := true: set(val): anonymous = val if is_node_ready() and not Engine.is_editor_hint(): ArchiveManager.archive.npc_anonymous_states[character_name] = val @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") func _ready() -> void: if Engine.is_editor_hint(): speaking_sign.visible = true return speaking_sign.visible = false speaking_sign.position.y = -height if character_name: var from_archive = false if ArchiveManager.archive.npc_anonymous_states.has(character_name): from_archive = true anonymous = ArchiveManager.archive.npc_anonymous_states[character_name] if GlobalConfig.DEBUG: print( "NPC [", character_name, "] is anonymous:", anonymous, " from archive:", from_archive ) if animation: play() sign_mark.interacted.connect(_on_interacted) sign_mark.cancel.connect(_stop) sign_mark.toggle_active.connect(_on_toggle_active) func _on_toggle_active(activated: bool) -> void: if activated: _speaking() else: _stop() func _on_interacted() -> void: # %Sfx.play() # _speaking() # play dialogue if dialogue_title: interacted.emit() # 适配匿名效果 if anonymous and not dialogue_title.ends_with(anonymous_title_suffix): dialogue_title += anonymous_title_suffix elif not anonymous and dialogue_title.ends_with(anonymous_title_suffix): dialogue_title = dialogue_title.substr( 0, dialogue_title.length() - anonymous_title_suffix.length() ) DialogueManager.show_dialogue_balloon(dialogue_res, dialogue_title) func _speaking() -> void: speaking_animation.play("speaking") func _stop() -> 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 } ]