@tool class_name PropHud extends Control @export_group("DebugItem") @export var enable_item := false: set(value): if value: enable_item = false enable_prop_item(item_key) @export var item_key: String @export_group("Inventory") @export var inventory: PropInventory @export_group("UI-UX") @export var display_time := 1.5 # 不包含渐入渐出(约 0.6s)的时长 @export var locked := false @export var selected := false: set(value): selected = value if value: %Mark.visible = true else: %Mark.visible = false @onready var sfx = %Sfx as Sfx @onready var left_btn = %LeftButton as TextureButton @onready var right_btn = %RightButton as TextureButton @onready var panel = %HudPanel as TextureButton @onready var prop = %Prop as TextureRect @onready var prop_container = %PropContainer as Container @onready var mark = %Mark as TextureRect @onready var title_label = %TitleLabel as Label var items_dict := {} var item_config_res = preload("res://asset/dialogue/item_description.dialogue") var item_config_title = "PropItems" var texture_path_prefix = "res://asset/art/prop/" var cached_inventory_textures := {} var listen_mouse = false var displaying = false var timer := Timer.new() var display_tween: Tween var container_tween: Tween func _ready() -> void: _load_items() _load_from_archive() if Engine.is_editor_hint(): return ArchiveManager.archive_loaded.connect(_load_from_archive) # tween timer timer.wait_time = display_time timer.one_shot = true timer.autostart = false timer.timeout.connect(toggle_details.bind(false)) add_child(timer) # connect signals left_btn.pressed.connect(on_left_pressed) right_btn.pressed.connect(on_right_pressed) panel.pressed.connect(_on_panel_pressed) mark.modulate.a = 0.0 title_label.modulate.a = 0.0 # _toggle_btn_ability(false) left_btn.modulate.a = 0.5 right_btn.modulate.a = 0.5 mouse_entered.connect(_on_mouse_entered) mouse_exited.connect(_on_mouse_exited) selected = selected func _load_items(): var current_line = await item_config_res.get_next_dialogue_line(item_config_title) while current_line: var texture_path = current_line.get_tag_value("texture") if texture_path: var item = PropItem.new() item.key = current_line.translation_key item.texture_path = texture_path_prefix + texture_path items_dict[item.key] = item if current_line.next_id == "end": break current_line = await item_config_res.get_next_dialogue_line(current_line.next_id) func _load_from_archive() -> void: if ArchiveManager.archive: inventory = ArchiveManager.archive.prop_inventory _load_texture_cache() _update_prop_display_with_texture() func _load_texture_cache() -> void: if not inventory: return cached_inventory_textures.clear() for key in inventory.enabled_items: # 以 items_dict 为准,如果 enabled_items 中的 key 不存在,则删除 if not key in items_dict: inventory.enabled_items.erase(key) continue var texture = load(items_dict[key].texture_path) as Texture2D if texture: cached_inventory_textures[key] = texture # wrap index inventory.current_index = wrapi(inventory.current_index, 0, inventory.enabled_items.size()) func _update_prop_display_with_texture(): if not inventory: return if inventory.enabled_items.size() == 0: prop.texture = null return var item = items_dict[inventory.current_item_key()] if not item: prop.texture = null push_error("PropItem is null! index=" + str(inventory.current_index)) return if item.key in cached_inventory_textures: prop.texture = cached_inventory_textures[item.key] else: prop.texture = null title_label.text = tr(item.key) func on_left_pressed() -> void: sfx.play() _mouse_moved_on_listening() inventory.index_wrap_add(-1) _update_prop_display_with_texture() _tween_container(true) func on_right_pressed() -> void: sfx.play() _mouse_moved_on_listening() inventory.index_wrap_add(1) _update_prop_display_with_texture() _tween_container(false) func _tween_container(left_to_right := true) -> void: if container_tween and container_tween.is_running(): container_tween.kill() container_tween = create_tween() if left_to_right: container_tween.tween_property(prop_container, "offset_left", 0.0, 0.3).from(-200.0) prop_container.offset_right = 0.0 else: container_tween.tween_property(prop_container, "offset_right", 0.0, 0.3).from(200.0) prop_container.offset_left = 0.0 func _on_panel_pressed() -> void: sfx.play() _mouse_moved_on_listening() print("panel") func _on_mouse_entered() -> void: listen_mouse = true _mouse_moved_on_listening() func _on_mouse_exited() -> void: listen_mouse = false func _mouse_moved_on_listening() -> void: if not displaying: toggle_details(true) return timer.start(display_time) func _unhandled_input(event: InputEvent) -> void: if locked: return if event.is_action_pressed("prop_left"): on_left_pressed() get_viewport().set_input_as_handled() elif event.is_action_pressed("prop_right"): on_right_pressed() get_viewport().set_input_as_handled() func _input(event: InputEvent) -> void: if locked: return if listen_mouse and (event is InputEventMouseMotion or event is InputEventScreenTouch): _mouse_moved_on_listening() func toggle_details(display := true) -> void: if display_tween and display_tween.is_running(): display_tween.kill() display_tween = create_tween() if display: displaying = true _toggle_btn_ability(true) display_tween.tween_property(mark, "modulate:a", 0.5, 0.2) display_tween.parallel().tween_property(title_label, "modulate:a", 1.0, 0.2) display_tween.parallel().tween_property(left_btn, "modulate:a", 1.0, 0.5) display_tween.parallel().tween_property(right_btn, "modulate:a", 1.0, 0.5) display_tween.tween_property(mark, "modulate:a", 1.0, 0.2) display_tween.parallel().tween_property(mark, "scale", Vector2(1.1, 1.1), 0.3).set_trans( Tween.TRANS_CUBIC ) display_tween.tween_property(mark, "scale", Vector2.ONE, 0.3).set_trans(Tween.TRANS_CUBIC) timer.start(display_time) else: displaying = false display_tween.tween_property(mark, "modulate:a", 0.0, 0.6) display_tween.parallel().tween_property(title_label, "modulate:a", 0.0, 0.6) display_tween.parallel().tween_property(left_btn, "modulate:a", 0.5, 0.5) display_tween.parallel().tween_property(right_btn, "modulate:a", 0.5, 0.5) # display_tween.tween_callback(_toggle_btn_ability.bind(false)) timer.stop() func _toggle_btn_ability(v: bool) -> void: left_btn.disabled = !v right_btn.disabled = !v func enable_prop_item(prop_key: String) -> void: if not inventory or not prop_key: return if not items_dict.has(prop_key): push_error("PropItem not found! key=" + prop_key) return inventory.enable_item(prop_key) _load_texture_cache() _update_prop_display_with_texture()