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-25 10:50:02 +00:00
|
|
|
# <0 means no walk to edge
|
2025-06-26 09:38:51 +00:00
|
|
|
@export var snap_to_edge := true
|
2025-06-25 10:50:02 +00:00
|
|
|
@export var walk_to_edge_width := 25.0
|
|
|
|
@export var action_key := 4
|
2025-06-13 08:03:19 +00:00
|
|
|
@export var enabled := true:
|
|
|
|
set(val):
|
|
|
|
enabled = val
|
|
|
|
if is_node_ready():
|
2025-06-29 14:57:02 +00:00
|
|
|
_align_signs_status()
|
|
|
|
@export var sign_mark_height := 10.0:
|
2025-02-12 07:03:41 +00:00
|
|
|
set(val):
|
2025-06-29 14:57:02 +00:00
|
|
|
sign_mark_height = val
|
2025-02-12 07:03:41 +00:00
|
|
|
if is_node_ready():
|
2025-06-29 14:57:02 +00:00
|
|
|
sign_mark.sign_mark_offset.y = -sign_mark_height
|
|
|
|
@export var speaking_sign_height := 60.0:
|
|
|
|
set(val):
|
|
|
|
speaking_sign_height = val
|
|
|
|
if is_node_ready():
|
|
|
|
speaking_sign.position.y = -speaking_sign_height
|
|
|
|
@export var sign_x_offset := 0.0:
|
|
|
|
set(val):
|
|
|
|
sign_x_offset = val
|
|
|
|
if is_node_ready():
|
|
|
|
speaking_sign.position.x = sign_x_offset
|
|
|
|
sign_mark.position.x = sign_x_offset
|
2025-06-25 10:50:02 +00:00
|
|
|
@export var collision_width_and_x := Vector2(20.0, 0):
|
|
|
|
set(val):
|
|
|
|
collision_width_and_x = val
|
|
|
|
if is_node_ready():
|
|
|
|
var shape = area2d.get_node("CollisionShape2D").shape
|
|
|
|
shape.size.x = collision_width_and_x.x
|
|
|
|
area2d.position.x = collision_width_and_x.y
|
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
|
2025-06-25 10:50:02 +00:00
|
|
|
@onready var sign_snapper = %SignSnapper as SignSnapper
|
2024-12-26 13:58:37 +00:00
|
|
|
@onready var area2d = %Area2D as Area2D
|
|
|
|
|
2025-06-29 14:57:02 +00:00
|
|
|
var ground_archive: GroundArchive
|
|
|
|
# 尝试互动的次数
|
|
|
|
var icount: int:
|
|
|
|
set(val):
|
|
|
|
icount = val
|
|
|
|
ground_archive.set_pair(name, "icount", val)
|
|
|
|
_align_signs_status()
|
|
|
|
|
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
|
|
|
|
2025-06-30 10:33:40 +00:00
|
|
|
var speaking_sign_tween: Tween
|
|
|
|
|
|
|
|
# 0 hide; 1 silent; 2 speaking
|
|
|
|
var speaking_sign_mode := 0:
|
|
|
|
set(val):
|
|
|
|
if speaking_sign_mode != val:
|
|
|
|
speaking_sign_mode = val
|
|
|
|
if speaking_sign_tween and speaking_sign_tween.is_valid():
|
|
|
|
speaking_sign_tween.kill()
|
|
|
|
speaking_sign_tween = create_tween()
|
|
|
|
if val == 0:
|
|
|
|
speaking_sign_tween.tween_property(speaking_sign, "modulate:a", 0.0, 0.3)
|
|
|
|
elif val == 1:
|
|
|
|
speaking_sign_tween.tween_property(speaking_sign, "modulate", base_mod, 0.3)
|
|
|
|
speaking_sign_tween.parallel().tween_property(
|
|
|
|
speaking_sign, "scale", base_scale, 0.3
|
|
|
|
)
|
|
|
|
speaking_animation.play("speaking")
|
|
|
|
elif val == 2:
|
|
|
|
speaking_sign_tween.tween_property(speaking_sign, "modulate", Color.WHITE, 0.3)
|
|
|
|
speaking_sign_tween.parallel().tween_property(
|
|
|
|
speaking_sign, "scale", base_scale * 1.3, 0.3
|
|
|
|
)
|
|
|
|
speaking_animation.play("speaking")
|
|
|
|
|
2025-06-25 10:50:02 +00:00
|
|
|
|
2024-12-26 13:58:37 +00:00
|
|
|
func _ready() -> void:
|
2025-06-29 14:57:02 +00:00
|
|
|
# sign position
|
|
|
|
sign_mark.sign_mark_offset.y = -sign_mark_height
|
|
|
|
speaking_sign.position.y = -speaking_sign_height
|
|
|
|
sign_mark.position.x = sign_x_offset
|
|
|
|
speaking_sign.position.x = sign_x_offset
|
|
|
|
# collisiong shape
|
2025-06-25 10:50:02 +00:00
|
|
|
var shape = area2d.get_node("CollisionShape2D").shape
|
|
|
|
shape.size.x = collision_width_and_x.x
|
|
|
|
area2d.position.x = collision_width_and_x.y
|
|
|
|
sign_snapper.action_on_arrived = action_key
|
2025-06-26 09:38:51 +00:00
|
|
|
sign_snapper.radius = walk_to_edge_width
|
|
|
|
sign_snapper.enabled = snap_to_edge
|
2025-06-30 10:33:40 +00:00
|
|
|
# 设置 speaking_sign 默认值
|
|
|
|
base_scale = speaking_sign.scale
|
|
|
|
base_mod = speaking_sign.modulate
|
|
|
|
speaking_sign.modulate.a = 0.0
|
2025-01-21 10:52:36 +00:00
|
|
|
if Engine.is_editor_hint():
|
2025-06-29 14:57:02 +00:00
|
|
|
# editor 下都显示
|
2025-02-12 07:03:41 +00:00
|
|
|
speaking_sign.visible = true
|
2025-06-30 10:33:40 +00:00
|
|
|
speaking_sign.modulate.a = 1.0
|
2025-06-29 14:57:02 +00:00
|
|
|
speaking_sign.get_node("Sprite2D").position.x = -60.0
|
|
|
|
speaking_sign.get_node("Sprite2D").frame = 2
|
|
|
|
sign_mark.display_sign = true
|
2025-01-21 10:52:36 +00:00
|
|
|
return
|
2025-06-29 14:57:02 +00:00
|
|
|
# setup default value
|
|
|
|
ground_archive = ArchiveManager.archive.ground_archive()
|
|
|
|
icount = ground_archive.get_value(name, "icount", 0)
|
2025-06-26 09:38:51 +00:00
|
|
|
if snap_to_edge:
|
2025-06-25 10:50:02 +00:00
|
|
|
sign_snapper.arrived.connect(_on_interacted)
|
|
|
|
else:
|
|
|
|
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-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-29 14:57:02 +00:00
|
|
|
_align_signs_status()
|
2025-06-13 08:03:19 +00:00
|
|
|
|
2025-06-14 08:46:32 +00:00
|
|
|
|
2025-06-29 14:57:02 +00:00
|
|
|
func _align_signs_status():
|
2025-06-28 13:42:20 +00:00
|
|
|
sign_mark.enabled = enabled
|
2025-06-29 14:57:02 +00:00
|
|
|
sign_mark.display_sign = icount == 0
|
|
|
|
speaking_sign.visible = enabled and icount > 0
|
2025-06-14 08:46:32 +00:00
|
|
|
|
2025-02-12 07:03:41 +00:00
|
|
|
|
|
|
|
func _on_toggle_active(activated: bool) -> void:
|
2025-06-30 10:33:40 +00:00
|
|
|
if activated and speaking_sign_mode == 0:
|
|
|
|
speaking_sign_mode = 1
|
|
|
|
elif not activated and speaking_sign_mode > 0:
|
|
|
|
speaking_sign_mode = 0
|
2024-12-27 07:56:45 +00:00
|
|
|
|
2025-06-25 10:50:02 +00:00
|
|
|
|
2024-12-26 13:58:37 +00:00
|
|
|
func _on_interacted() -> void:
|
2025-01-10 09:53:12 +00:00
|
|
|
# play dialogue
|
|
|
|
if dialogue_title:
|
2025-06-28 16:11:21 +00:00
|
|
|
if GlobalConfig.DEBUG:
|
|
|
|
print("[" + name + "] call lock")
|
|
|
|
SceneManager.lock_player(0, action_key)
|
2025-06-29 14:57:02 +00:00
|
|
|
icount += 1
|
|
|
|
ground_archive.set_pair(name, "icount", icount)
|
|
|
|
DialogueManager.show_dialogue_balloon(dialogue_res, dialogue_title)
|
|
|
|
interacted.emit()
|
2025-06-30 10:33:40 +00:00
|
|
|
speaking_sign_mode = 2
|
2025-06-28 16:11:21 +00:00
|
|
|
await DialogueManager.dialogue_ended
|
2025-06-30 10:33:40 +00:00
|
|
|
speaking_sign_mode = 1
|
2025-06-28 16:11:21 +00:00
|
|
|
if GlobalConfig.DEBUG:
|
|
|
|
print("[" + name + "] call lock")
|
|
|
|
SceneManager.unlock_player()
|
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
|
|
|
}
|
|
|
|
]
|