xiandie/scene/entity/inspectable.gd
2025-06-26 01:53:16 +08:00

240 lines
7.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@tool
extends Sprite2D
signal start_inspecting
signal quit_inspecting
signal sign_mark_offset_updated
# @export var entity_config: EntityConfig:
# set(value):
# entity_config = value
enum { STATUS_NORAML, STATUS_TRANSITIONING, STATUS_INSPECTING_COVER, STATUS_INSPECTING_NOTES }
@export var enabled := true:
set(val):
enabled = val
if is_node_ready() and not Engine.is_editor_hint():
sign_mark.enabled = val
# sign_mark 节点在 ready 时会直接读取
@export var sign_mark_offset := Vector2.ZERO:
set(val):
sign_mark_offset = val
sign_mark_offset_updated.emit(val)
# @export var entity_name: String = ""
# 第一次交互时的气泡文字unrevealed -> revealed 如果为空则跳过
@export var first_interact_os_key := ""
@export var content_centered: bool = false
@export var action_key := 4
@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
@export var texture_cover: Texture2D
@export var note_sign_texture: Texture2D
@export_enum("none", "c01", "c02", "c03", "c04", "c05", "c06") var editor_filter := "none":
set(val):
editor_filter = val
if is_node_ready() and Engine.is_editor_hint():
notify_property_list_changed()
var content_key: String = ""
@onready var sign_mark = %Sign as Sign
@onready var sign_snapper = %SignSnapper as SignSnapper
@onready var area2d = %Area2D as Area2D
@onready var sfx = %Sfx
@onready var container = %Container
@onready var cover_rect = %Cover as TextureRect
@onready var content_label = %ContentLabel as Label
@onready var tip_label = %TipLabel as Label
var tip_cover_hide_notes = "Q: " + tr("ui_退出") + " " + "E: " + tr("ui_阅读")
var tip_cover_without_notes = "Q: " + tr("ui_退出") + " " + "E: " + tr("ui_检阅")
var tip_notes = "Q: " + tr("ui_退出") + " " + "E: " + tr("ui_收起")
static var content_dialogue = (
preload("res://asset/dialogue/inspect_content.dialogue") as DialogueResource
)
var status := STATUS_NORAML
var blinking_tween: Tween
var ground_archive: GroundArchive
# 尝试互动的次数
var tried_times: int:
set(val):
tried_times = val
ground_archive.set_pair(name, "tried_times", val)
if tried_times >= 1 and sign_mark:
sign_mark.sprite2d.texture = note_sign_texture
func _ready() -> void:
$InspectLayer.layer = GlobalConfig.CANVAS_LAYER_PROP_INSPECTOR
var shape = area2d.get_node("CollisionShape2D").shape
shape.size.x = collision_width_and_x.x
area2d.position.x = collision_width_and_x.y
container.modulate.a = 0.0
content_label.modulate.a = 0.0
content_label.text = _get_tr_content()
tip_label.text = tip_cover_hide_notes
cover_rect.texture = texture_cover
if Engine.is_editor_hint():
return
sign_mark.enabled = enabled
# setup default value
ground_archive = ArchiveManager.archive.ground_archive()
tried_times = ground_archive.get_value(name, "tried_times", 0)
if content_centered:
content_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
# sign_mark.interacted.connect(_on_interacted)
sign_snapper.arrived.connect(_on_interacted)
sign_mark.cancel.connect(_on_cancel)
func _get_tr_content():
if content_key == "":
return ""
var inspection_note = ""
# get note content
var line_id = content_dialogue.titles.get(content_key)
while line_id and line_id != "end":
var line = content_dialogue.lines[line_id]
if line.has("text"):
inspection_note += tr(line.get("text")) + "\n"
line_id = line.get("next_id")
return inspection_note
func _on_interacted() -> void:
if STATUS_TRANSITIONING == status:
return
if tried_times == 0 and first_interact_os_key:
SceneManager.freeze_player(0, action_key)
sign_mark.display_sign = false
var tween = await SceneManager.pop_os_with_str(first_interact_os_key)
tween.tween_callback(_do_action)
tween.tween_callback(func(): sign_mark.display_sign = true)
else:
SceneManager.freeze_player(0, action_key)
_do_action()
func _do_action() -> void:
if STATUS_TRANSITIONING == status:
return
if status == STATUS_NORAML:
tried_times += 1
sfx.play()
SceneManager.focus_node(self)
SceneManager.get_camera_marker().tween_zoom(2.0)
status = STATUS_TRANSITIONING
var tween = create_tween()
tween.tween_interval(0.8)
tween.tween_property(container, "modulate:a", 1.0, 0.7)
tween.tween_callback(func(): status = STATUS_INSPECTING_COVER)
# 改变信号源
sign_mark.interacted.connect(_on_interacted)
sign_snapper.arrived.disconnect(_on_interacted)
sign_mark.display_sign = false
SceneManager.freeze_player(0.0, action_key)
cover_rect.texture = texture_cover
if content_key == "":
tip_label.text = tip_cover_without_notes
else:
tip_label.text = tip_cover_hide_notes
_blink_label(true)
start_inspecting.emit()
elif status == STATUS_INSPECTING_COVER:
sfx.play()
status = STATUS_INSPECTING_NOTES
tip_label.text = tip_notes
create_tween().tween_property(content_label, "modulate:a", 1.0, 0.15)
elif status == STATUS_INSPECTING_NOTES:
sfx.play()
status = STATUS_INSPECTING_COVER
if content_key == "":
tip_label.text = tip_cover_without_notes
else:
tip_label.text = tip_cover_hide_notes
create_tween().tween_property(content_label, "modulate:a", 0.0, 0.15)
func _blink_label(init := true):
if status == STATUS_NORAML:
return
blinking_tween = create_tween()
if init:
blinking_tween.tween_property(tip_label, "modulate:a", 0.8, 2.0)
blinking_tween.tween_callback(_blink_label.bind(false))
else:
blinking_tween.tween_property(tip_label, "modulate:a", 0.5, 1.0)
blinking_tween.tween_callback(_blink_label.bind(true))
func _on_cancel(_body = null):
if STATUS_TRANSITIONING == status:
return
if status != STATUS_NORAML:
quit_inspecting.emit()
status = STATUS_TRANSITIONING
var tween = create_tween()
tween.tween_property(container, "modulate:a", 0.0, 0.15)
if blinking_tween and blinking_tween.is_running():
blinking_tween.kill()
tween.parallel().tween_property(tip_label, "modulate:a", 0.0, 0.15)
tween.tween_interval(1.0)
tween.tween_callback(func(): status = STATUS_NORAML)
tween.tween_callback(func(): content_label.modulate.a = 0.0)
SceneManager.focus_player_and_reset_zoom()
SceneManager.release_player()
sign_mark.display_sign = true
# 改变信号源
sign_mark.interacted.disconnect(_on_interacted)
sign_snapper.arrived.connect(_on_interacted)
func _set(property: StringName, value: Variant) -> bool:
if property == "content_key":
content_key = value
content_label.text = _get_tr_content()
return true
return false
func _get(property: StringName) -> Variant:
if property == "content_key":
return content_key
return null
func _get_property_list() -> Array[Dictionary]:
var titles = ""
# only show notes_ properties in editor
if Engine.is_editor_hint():
var ordered_titles = content_dialogue.get_ordered_titles()
if editor_filter and editor_filter != "none":
var filted_titles = ordered_titles.filter(_filter_property)
if filted_titles.size() > 0:
titles = ",".join(filted_titles)
else:
titles = ",".join(ordered_titles)
return [
{
"name": "content_key",
"type": TYPE_STRING,
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
"hint_string": titles
}
]
func _filter_property(property: StringName) -> bool:
return property.find(editor_filter) >= 0