2025-01-10 09:53:12 +00:00
|
|
|
@tool
|
2025-05-14 20:43:55 +00:00
|
|
|
class_name Npc2D extends AnimatedSprite2D
|
|
|
|
|
|
|
|
signal interacted
|
2024-12-26 13:58:37 +00:00
|
|
|
|
2025-06-13 08:03:19 +00:00
|
|
|
@export var enabled := true:
|
|
|
|
set(val):
|
|
|
|
enabled = val
|
|
|
|
if is_node_ready():
|
|
|
|
_check_sign_mark_enable()
|
2025-02-12 07:03:41 +00:00
|
|
|
@export var height := 60.0:
|
|
|
|
set(val):
|
|
|
|
height = val
|
|
|
|
if is_node_ready():
|
|
|
|
speaking_sign.position.y = -height
|
2024-12-26 13:58:37 +00:00
|
|
|
|
2024-12-27 07:56:45 +00:00
|
|
|
@onready var speaking_animation = %SpeakingAnimationPlayer
|
|
|
|
@onready var speaking_sign = %SpeakingSign2D as Node2D
|
2025-01-08 00:51:09 +00:00
|
|
|
@onready var sign_mark = %Sign as Sign
|
2024-12-26 13:58:37 +00:00
|
|
|
@onready var area2d = %Area2D as Area2D
|
|
|
|
|
2025-01-10 09:53:12 +00:00
|
|
|
var dialogue_title := ""
|
|
|
|
var dialogue_res = preload("res://asset/dialogue/npc.dialogue")
|
|
|
|
|
2025-06-13 08:03:19 +00:00
|
|
|
var base_scale := Vector2.ONE
|
|
|
|
var base_mod := Color.WHITE_SMOKE
|
2025-01-10 09:53:12 +00:00
|
|
|
|
2024-12-26 13:58:37 +00:00
|
|
|
func _ready() -> void:
|
2025-06-14 08:46:32 +00:00
|
|
|
speaking_sign.position.y = -height
|
2025-01-21 10:52:36 +00:00
|
|
|
if Engine.is_editor_hint():
|
2025-06-14 08:46:32 +00:00
|
|
|
# editor 直接展示
|
2025-02-12 07:03:41 +00:00
|
|
|
speaking_sign.visible = true
|
2025-06-14 08:46:32 +00:00
|
|
|
speaking_sign.modulate.a = 1.0
|
2025-01-21 10:52:36 +00:00
|
|
|
return
|
2025-06-14 08:46:32 +00:00
|
|
|
# 默认为 0
|
|
|
|
speaking_sign.modulate.a = 0.0
|
2025-06-13 08:03:19 +00:00
|
|
|
base_scale = speaking_sign.scale
|
|
|
|
base_mod = speaking_sign.modulate
|
2025-06-14 08:46:32 +00:00
|
|
|
area2d.body_entered.connect(_on_entered)
|
|
|
|
area2d.body_exited.connect(_on_exited)
|
2024-12-26 13:58:37 +00:00
|
|
|
sign_mark.interacted.connect(_on_interacted)
|
2025-06-13 08:03:19 +00:00
|
|
|
# sign_mark.cancel.connect(_stop_speaking)
|
2025-02-12 07:03:41 +00:00
|
|
|
sign_mark.toggle_active.connect(_on_toggle_active)
|
2025-06-13 08:03:19 +00:00
|
|
|
_check_sign_mark_enable()
|
2025-06-06 16:19:37 +00:00
|
|
|
visibility_changed.connect(_on_visibility_changed)
|
2025-06-13 08:03:19 +00:00
|
|
|
if sprite_frames and animation:
|
|
|
|
play()
|
2025-06-06 16:19:37 +00:00
|
|
|
|
2025-06-14 08:46:32 +00:00
|
|
|
|
2025-06-06 16:19:37 +00:00
|
|
|
func _on_visibility_changed() -> void:
|
2025-06-13 08:03:19 +00:00
|
|
|
_check_sign_mark_enable()
|
|
|
|
|
2025-06-14 08:46:32 +00:00
|
|
|
|
2025-06-13 08:03:19 +00:00
|
|
|
func _check_sign_mark_enable():
|
2025-06-14 08:46:32 +00:00
|
|
|
speaking_sign.visible = enabled
|
2025-06-13 08:03:19 +00:00
|
|
|
sign_mark.enabled = enabled and visible
|
2025-02-12 07:03:41 +00:00
|
|
|
|
2025-06-14 08:46:32 +00:00
|
|
|
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)
|
|
|
|
|
2025-02-12 07:03:41 +00:00
|
|
|
|
|
|
|
func _on_toggle_active(activated: bool) -> void:
|
|
|
|
if activated:
|
2025-06-13 08:03:19 +00:00
|
|
|
_play_speaking()
|
2025-02-12 07:03:41 +00:00
|
|
|
else:
|
2025-06-13 08:03:19 +00:00
|
|
|
_stop_speaking()
|
2024-12-26 13:58:37 +00:00
|
|
|
|
2025-06-13 08:03:19 +00:00
|
|
|
var speaking := false
|
2024-12-27 07:56:45 +00:00
|
|
|
|
2024-12-26 13:58:37 +00:00
|
|
|
func _on_interacted() -> void:
|
2025-06-13 08:03:19 +00:00
|
|
|
if speaking:
|
|
|
|
return
|
2025-01-10 09:53:12 +00:00
|
|
|
# play dialogue
|
|
|
|
if dialogue_title:
|
2025-05-14 20:43:55 +00:00
|
|
|
interacted.emit()
|
2025-01-10 09:53:12 +00:00
|
|
|
DialogueManager.show_dialogue_balloon(dialogue_res, dialogue_title)
|
2025-06-13 08:03:19 +00:00
|
|
|
speaking = true
|
2025-06-06 16:19:37 +00:00
|
|
|
SceneManager.freeze_player(0)
|
|
|
|
DialogueManager.dialogue_ended.connect(_dialog_end, CONNECT_ONE_SHOT)
|
2025-06-13 08:03:19 +00:00
|
|
|
var tween = create_tween()
|
|
|
|
tween.tween_property(speaking_sign, "modulate", Color.WHITE, 0.5)
|
2025-06-14 08:46:32 +00:00
|
|
|
tween.parallel().tween_property(speaking_sign, "scale", base_scale * 1.3, 0.3)
|
2025-06-06 16:19:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
func _dialog_end(_res):
|
2025-06-13 08:03:19 +00:00
|
|
|
speaking = false
|
2025-06-06 16:19:37 +00:00
|
|
|
SceneManager.release_player()
|
2025-06-13 08:03:19 +00:00
|
|
|
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)
|
2024-12-26 13:58:37 +00:00
|
|
|
|
|
|
|
|
2025-06-13 08:03:19 +00:00
|
|
|
func _play_speaking() -> void:
|
2025-02-12 07:03:41 +00:00
|
|
|
speaking_animation.play("speaking")
|
2024-12-27 07:56:45 +00:00
|
|
|
|
2024-12-26 13:58:37 +00:00
|
|
|
|
2025-06-13 08:03:19 +00:00
|
|
|
func _stop_speaking() -> void:
|
2025-02-12 07:03:41 +00:00
|
|
|
speaking_animation.play("RESET")
|
2025-01-10 09:53:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
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]:
|
2025-01-23 02:01:53 +00:00
|
|
|
var hint_str = ""
|
|
|
|
if Engine.is_editor_hint():
|
|
|
|
hint_str = ",".join(dialogue_res.get_ordered_titles())
|
2025-01-10 09:53:12 +00:00
|
|
|
return [
|
|
|
|
{
|
|
|
|
"name": "dialogue_title",
|
|
|
|
"type": TYPE_STRING,
|
|
|
|
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
|
2025-01-23 02:01:53 +00:00
|
|
|
"hint_string": hint_str
|
2025-01-10 09:53:12 +00:00
|
|
|
}
|
|
|
|
]
|