@tool class_name PropInspector extends CanvasLayer enum { STATUS_HIDDEN, STATUS_INSPECTING_PROP, STATUS_INSPECTING_COVER, STATUS_INSPECTING_NOTES } signal quit_and_hidden @onready var notes_bg = %NotesBG as TextureRect @onready var prop_bg = %PropBG as TextureRect @onready var full_texture = %FullTexture as TextureRect @onready var scroll_container = %ScrollContainer as ScrollContainer @onready var content_label = %ContentLabel as Label @onready var tip_label = %TipLabel as Label @onready var inspector_balloon = %InspectorBalloon as InspectorBalloon var tip_cover = "Q: " + tr("ui_退出") + " " + "E: " + tr("ui_阅读") var tip_notes = "Q: " + tr("ui_退出") + " " + "E: " + tr("ui_收起") var texture_cover: Texture2D var texture_notes: Texture2D var status := STATUS_HIDDEN var blinking_tween: Tween # Called when the node enters the scene tree for the first time. func _ready() -> void: tip_label.text = tip_cover if Engine.is_editor_hint(): return visible = false layer = GlobalConfig.CANVAS_LAYER_PROP_INSPECTOR prop_bg.modulate.a = 0.0 full_texture.modulate.a = 0.0 content_label.modulate.a = 0.0 tip_label.modulate.a = 0.0 inspector_balloon.quitted.connect(_on_inspector_balloon_quitted) func _on_inspector_balloon_quitted(_is_manually: bool): _hide() var hiding_tween: Tween func _hide(): if status == STATUS_HIDDEN: return status = STATUS_HIDDEN # 延迟完善 hide 的过程 if hiding_tween and hiding_tween.is_running(): hiding_tween.kill() quit_and_hidden.emit() hiding_tween = create_tween() hiding_tween.parallel().tween_property(full_texture, "modulate:a", 0.0, 0.3) hiding_tween.parallel().tween_property(content_label, "modulate:a", 0.0, 0.15) if blinking_tween and blinking_tween.is_running(): blinking_tween.kill() hiding_tween.parallel().tween_property(tip_label, "modulate:a", 0.0, 0.15) hiding_tween.tween_callback(_post_hide) func _post_hide(): # hiding_tween 被 kill 的时候需要调用 _post_hide 或 quit_and_hidden.emit() quit_and_hidden.emit() scroll_container.mouse_filter = Control.MOUSE_FILTER_IGNORE locking = false prop_bg.visible = false full_texture.texture = null texture_cover = null texture_notes = null content_label.text = "" tip_label.text = tip_cover notes_bg.visible = false visible = false func _blink_label(init := true): if status == STATUS_HIDDEN: 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)) # 如果没有 notes_texture,自动使用黑色遮罩 func pop_standard_inspection( cover_texture, notes_texture, inspection_note, centered := false, wide := false ): if status == STATUS_INSPECTING_PROP: inspector_balloon.kill_without_signal() if hiding_tween and hiding_tween.is_running(): hiding_tween.kill() _post_hide() locking = true status = STATUS_INSPECTING_COVER visible = true full_texture.texture = cover_texture texture_cover = cover_texture texture_notes = notes_texture if centered: content_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER else: content_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT if wide: content_label.custom_minimum_size.x = 250 else: content_label.custom_minimum_size.x = 150 # # shrink back # content_label.size.x = 150 content_label.text = inspection_note.replace("{br}", "\n").strip_edges() var tween = create_tween() tween.tween_property(full_texture, "modulate:a", 1.0, 0.15) tip_label.text = tip_cover _blink_label() var _lock_mutex := Mutex.new() var locking = false: set(val): _lock_mutex.lock() if val != locking: locking = val if val: SceneManager.lock_player() else: SceneManager.unlock_player() _lock_mutex.unlock() func pop_prop_inspection( prop_key: String, cover_texture: Texture2D, display_obtained: bool ): if not cover_texture: push_error("PropInspector: cover_texture is not set") return if hiding_tween and hiding_tween.is_running(): hiding_tween.kill() _post_hide() locking = true status = STATUS_INSPECTING_PROP visible = true var tween = create_tween() prop_bg.visible = true full_texture.texture = cover_texture tween.tween_property(full_texture, "modulate:a", 1.0, 0.15) content_label.text = "" tip_label.text = "" # 显示道具获得提示 if prop_key: var prop_title = tr(prop_key) # 道具的一句话说明 var original_word_lines = tr(prop_key + "_说明").replace("{br}", "\n").split("\n") # 缩略只要第一行 var content = original_word_lines[0] + ("..." if len(original_word_lines) > 1 else "") if display_obtained: var obtain_str = tr("ui_获得道具") inspector_balloon.show_prop_balloon(obtain_str, prop_title, content) else: inspector_balloon.show_prop_content(content) func _show_prop_words(line_id: String): # 如果手动退出 ballon,同时退出 prop inspector if line_id == "3": _hide() func _unhandled_input(event: InputEvent) -> void: if status == STATUS_HIDDEN: return if ( status == STATUS_INSPECTING_NOTES and event is InputEventMouseButton and ( event.button_index == MOUSE_BUTTON_WHEEL_DOWN or event.button_index == MOUSE_BUTTON_WHEEL_UP ) ): # STATUS_INSPECTING_NOTES 状态下,消费鼠标滚轮事件 get_viewport().set_input_as_handled() # print("[prop inspector] mouse wheel handled on STATUS_INSPECTING_NOTES") if event.is_action_pressed("cancel"): get_viewport().set_input_as_handled() _hide() if event.is_action_pressed("interact"): get_viewport().set_input_as_handled() scroll_container.mouse_filter = Control.MOUSE_FILTER_IGNORE # STATUS_INSPECTING_COVER 与 STATUS_INSPECTING_NOTES 之间互相切换 if status == STATUS_INSPECTING_COVER: # inspect notes status = STATUS_INSPECTING_NOTES scroll_container.mouse_filter = Control.MOUSE_FILTER_PASS if not texture_notes: notes_bg.visible = true else: full_texture.texture = texture_notes tip_label.text = tip_notes create_tween().tween_property(content_label, "modulate:a", 1.0, 0.2) elif status == STATUS_INSPECTING_NOTES: # inspect cover status = STATUS_INSPECTING_COVER notes_bg.visible = false full_texture.texture = texture_cover tip_label.text = tip_cover create_tween().tween_property(content_label, "modulate:a", 0.0, 0.2) elif status == STATUS_INSPECTING_PROP: # # STATUS_INSPECTING_PROP 直接退出 # # 因为 input 优先被 ballon 获得,然后被 inspector 获得 # # 所以此时必然没有 ballon 存在 _hide()