xiandie/scene/entity/npc.gd

131 lines
3.2 KiB
GDScript3
Raw Normal View History

2025-01-10 09:53:12 +00:00
@tool
2025-05-14 20:43:55 +00:00
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
2025-01-08 00:51:09 +00:00
@onready var sign_mark = %Sign as Sign
@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")
var base_scale := Vector2.ONE
var base_mod := Color.WHITE_SMOKE
2025-01-10 09:53:12 +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 直接展示
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
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)
sign_mark.interacted.connect(_on_interacted)
# sign_mark.cancel.connect(_stop_speaking)
sign_mark.toggle_active.connect(_on_toggle_active)
_check_sign_mark_enable()
2025-06-06 16:19:37 +00:00
visibility_changed.connect(_on_visibility_changed)
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:
_check_sign_mark_enable()
2025-06-14 08:46:32 +00:00
func _check_sign_mark_enable():
2025-06-14 08:46:32 +00:00
speaking_sign.visible = enabled
sign_mark.enabled = enabled and visible
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)
func _on_toggle_active(activated: bool) -> void:
if activated:
_play_speaking()
else:
_stop_speaking()
var speaking := false
func _on_interacted() -> void:
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)
speaking = true
2025-06-06 16:19:37 +00:00
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)
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):
speaking = false
2025-06-06 16:19:37 +00:00
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")
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]:
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,
"hint_string": hint_str
2025-01-10 09:53:12 +00:00
}
]