xiandie/scene/ux/prop_hud.gd

683 lines
19 KiB
GDScript3
Raw Normal View History

# @tool
2025-01-07 10:54:50 +00:00
class_name PropHud extends Control
2025-01-13 08:09:57 +00:00
signal current_item_changed(prop_key: String)
2025-07-16 08:32:40 +00:00
# 常量定义
const PROP_CONTAINER_X = 130.0
const PROP_CONTROL_X = 110.0
const TWEEN_DURATION = 0.5
const SHAKE_FPS = 15.0
const SHAKE_DURATION = 0.8
const SHAKE_DELTA = 12.0
@export_group("DebugItem")
@export var enable_item := false:
set(value):
if value:
enable_item = false
enable_prop_item(item_key)
2025-01-10 07:43:55 +00:00
@export var item_key: String
2025-07-16 08:32:40 +00:00
@export_group("Inventory")
2025-01-13 08:09:57 +00:00
@export var inventory: PropInventory:
set(value):
2025-07-16 08:32:40 +00:00
if inventory == value:
return
if inventory and inventory.current_item_changed.is_connected(_emit_changed):
inventory.current_item_changed.disconnect(_emit_changed)
2025-01-13 08:09:57 +00:00
inventory = value
if inventory and not inventory.current_item_changed.is_connected(_emit_changed):
inventory.current_item_changed.connect(_emit_changed)
2025-07-16 08:32:40 +00:00
@export_group("UI-UX")
@export var display_time := 2.5 # 不包含渐入渐出(约 0.6s)的时长
2025-07-09 15:04:08 +00:00
@export var locked := false
2025-01-13 08:09:57 +00:00
@onready var sfx_click = %SfxClick as Sfx
@onready var sfx_inspect = %SfxInspect as Sfx
2025-04-20 13:35:13 +00:00
@onready var sfx_new_prop = %SfxNewProp as Sfx
@onready var left_btn = %LeftButton as TextureButton
@onready var right_btn = %RightButton as TextureButton
@onready var title_label = %TitleLabel as Label
# hud_rect 为背景, prop_scroll 为滚动区域prop_hbox 为道具容器
@onready var hud_rect = %HudRect as NinePatchRect
@onready var prop_scroll = %PropScrollContainer as ScrollContainer
@onready var prop_hbox = %PropHBox as HBoxContainer
@onready var display_prop = %DiaplayProp as TextureButton
@onready var selecting_bg = %SelectingBG as TextureRect
# hud_rect 最左侧为 prop_scroll, 最右侧为 props_bag_scroll
@onready var props_bag_scroll = %PropsBagScroll as ScrollContainer
@onready var props_bag = %PropsBag as HBoxContainer
@onready var select_mark = %SelectMark as TextureRect
2025-07-16 08:32:40 +00:00
var prop_containers: Array[CenterContainer] = []
var items_dict := {}
var items_description_dict = {}
# 从配置文件加载 prop items
var item_config_res = preload("uid://b1vwhxctfhl5d") #item_description.dialogue
2025-01-13 08:09:57 +00:00
var path_prefix = "res://asset/art/prop/"
var cached_inventory_textures := {}
# hud 展示与隐藏动效
var listen_mouse = false
var displaying = false
var timer := Timer.new()
var display_tween: Tween
var container_tween: Tween
2025-07-16 08:32:40 +00:00
var prop_blink: Tween
var tween_scroll: Tween
var shake_tween: Tween
# hud 是否监听快捷键
var listening_hotkey = true
2025-07-16 08:32:40 +00:00
# 缓存的占位符纹理
var placeholder_texture: Texture2D
func _emit_changed(prop_key := ""):
2025-07-09 15:04:08 +00:00
current_item_changed.emit(prop_key)
func _ready() -> void:
if Engine.is_editor_hint():
return
2025-07-16 08:32:40 +00:00
# 初始化占位符纹理 placeholder.png
placeholder_texture = preload("uid://djrfdhywg7uu2")
2025-07-16 08:32:40 +00:00
# read prop containers
for id in range(props_bag.get_child_count()):
var container = props_bag.get_child(id)
prop_containers.append(container)
2025-07-16 08:32:40 +00:00
var button = container.get_child(0).get_child(0)
if button:
button.gui_input.connect(_on_prop_gui_input.bind(id))
display_prop.gui_input.connect(_on_prop_gui_input.bind(-1))
2025-07-16 08:32:40 +00:00
_load_items_config_to_dict("ImportantPropItems")
_load_items_config_to_dict("PropItems")
2025-07-09 13:12:28 +00:00
_reload_cache_and_realign_display()
2025-07-16 08:32:40 +00:00
selecting_bg.modulate.a = 0.0
prop_scroll.scroll_horizontal = PROP_CONTAINER_X
props_bag_scroll.scroll_horizontal = 0.0
props_bag_scroll.custom_minimum_size.x = 0.0
2025-07-16 08:32:40 +00:00
# 存档更新时,从存档加载 prop
2025-07-09 13:12:28 +00:00
ArchiveManager.archive_loaded.connect(_reload_cache_and_realign_display)
2025-07-16 08:32:40 +00:00
# tween timer
timer.wait_time = display_time
timer.one_shot = true
timer.autostart = false
timer.timeout.connect(_on_timer_timeout)
add_child(timer)
2025-07-16 08:32:40 +00:00
# connect signals
left_btn.pressed.connect(on_left_pressed)
right_btn.pressed.connect(on_right_pressed)
title_label.modulate.a = 0.0
left_btn.modulate.a = 0.5
right_btn.modulate.a = 0.5
2025-01-07 10:54:50 +00:00
mouse_entered.connect(_on_mouse_entered)
mouse_exited.connect(_on_mouse_exited)
func _load_items_config_to_dict(title: String):
2025-07-16 08:32:40 +00:00
if not item_config_res or not item_config_res.titles.has(title):
return
var id = item_config_res.titles[title]
2025-07-16 08:32:40 +00:00
var current_line = item_config_res.lines.get(id)
while current_line:
2025-01-12 12:15:18 +00:00
if current_line.has("tags") and current_line.has("translation_key"):
var texture_path = ""
2025-01-13 08:09:57 +00:00
var inspect_path = ""
2025-07-16 08:32:40 +00:00
for tag in current_line.tags:
if tag.begins_with("texture="):
texture_path = tag.substr(8).strip_edges()
elif tag.begins_with("inspect="):
inspect_path = tag.substr(8).strip_edges()
var item = PropItem.new()
item.key = current_line.translation_key
2025-01-13 08:09:57 +00:00
if texture_path:
item.texture_path = path_prefix + texture_path
if inspect_path:
item.inspect_path = path_prefix + inspect_path
2025-07-16 08:32:40 +00:00
items_dict[item.key] = item
items_description_dict[item.key] = tr(item.key + "_说明").replace("{br}", "\n")
2025-01-12 12:15:18 +00:00
if not current_line.has("next_id") or current_line.next_id == "end":
break
2025-07-16 08:32:40 +00:00
current_line = item_config_res.lines.get(current_line.next_id)
2025-07-09 13:12:28 +00:00
func _reload_cache_and_realign_display() -> void:
if ArchiveManager.archive:
inventory = ArchiveManager.archive.prop_inventory
_load_texture_cache()
2025-07-09 13:12:28 +00:00
_align_container_size()
_update_prop_display_with_texture()
2025-06-24 17:52:30 +00:00
func checkout_inventory(character: String) -> void:
if not inventory:
printerr("PropHud checkout_inventory: No inventory found.")
return
inventory.checkout(character)
2025-07-09 13:12:28 +00:00
_reload_cache_and_realign_display()
2025-07-16 08:32:40 +00:00
const HUD_FADE_DURATION = 0.3
# 在变量定义区域添加
var hud_visibility_tween: Tween
func hide_hud(duration: float = HUD_FADE_DURATION):
if not visible:
return
listening_hotkey = false
2025-07-16 08:32:40 +00:00
# 清理之前的补间动画
if hud_visibility_tween and hud_visibility_tween.is_running():
hud_visibility_tween.kill()
# 创建渐变动画
hud_visibility_tween = create_tween()
hud_visibility_tween.tween_property(self, "modulate:a", 0.0, duration)
hud_visibility_tween.tween_callback(hide)
2025-07-16 08:32:40 +00:00
func display_hud(duration: float = HUD_FADE_DURATION):
if visible and modulate.a >= 1.0:
return
# 清理之前的补间动画
if hud_visibility_tween and hud_visibility_tween.is_running():
hud_visibility_tween.kill()
# 确保节点可见但透明
if not visible:
modulate.a = 0.0
show()
# 创建渐变动画
hud_visibility_tween = create_tween()
hud_visibility_tween.tween_property(self, "modulate:a", 1.0, duration)
hud_visibility_tween.tween_callback(func(): listening_hotkey = true)
func _load_texture_cache() -> void:
if not inventory:
return
2025-07-16 08:32:40 +00:00
# 移除无效纹理
var keys_to_remove = []
for k in cached_inventory_textures:
if not k in inventory.enabled_items:
2025-07-16 08:32:40 +00:00
keys_to_remove.append(k)
for k in keys_to_remove:
cached_inventory_textures.erase(k)
# 加载新纹理
for key in inventory.enabled_items:
if cached_inventory_textures.has(key):
continue
2025-07-16 08:32:40 +00:00
if not items_dict.has(key):
2025-06-24 17:52:30 +00:00
inventory.disable_item(key)
2025-07-09 13:12:28 +00:00
printerr(
"PropHud _load_texture_cache: key not found in items_dict:", key, ". remove item."
)
continue
2025-07-16 08:32:40 +00:00
2025-01-13 08:09:57 +00:00
var path = items_dict[key].texture_path
if not path:
2025-07-16 08:32:40 +00:00
cached_inventory_textures[key] = placeholder_texture
print("Cache load prop placeholder key=", key)
2025-01-13 08:09:57 +00:00
continue
2025-07-16 08:32:40 +00:00
2025-01-13 08:09:57 +00:00
var texture = load(path) as Texture2D
if texture:
if GlobalConfig.DEBUG:
print("Cache load prop texture key=", key)
cached_inventory_textures[key] = texture
2025-07-16 08:32:40 +00:00
# wrap index
2025-07-16 08:32:40 +00:00
if inventory.enabled_items.size() > 0:
inventory.current_index = wrapi(inventory.current_index, 0, inventory.enabled_items.size())
func _update_prop_display_with_texture():
if not inventory:
return
2025-07-16 08:32:40 +00:00
var key = ""
# 在没有道具时,展示空手 placeholder
if inventory.enabled_items.is_empty():
2025-07-16 08:32:40 +00:00
display_prop.texture_normal = placeholder_texture
display_prop.size = Vector2(PROP_CONTROL_X, PROP_CONTROL_X)
display_prop.scale = Vector2(1.0, 1.0)
title_label.text = tr("prop_空手")
else:
key = inventory.current_item_key()
_display_texture_by_key(display_prop, key)
title_label.text = tr(key)
2025-07-16 08:32:40 +00:00
# 如果当前是 prop_小猫玩具完整尝试点亮玩家的灯效否则无需点亮
var player = SceneManager.get_player()
if player:
player.set_catty_light(key == "prop_小猫玩具完整")
2025-07-16 08:32:40 +00:00
2025-07-15 17:53:09 +00:00
if GlobalConfig.DEBUG:
print("[PropHud] current display prop:", key)
2025-07-16 08:32:40 +00:00
# 选中标记处理
_update_select_mark()
# bag更新
for i in range(inventory.enabled_items.size()):
var id = wrapi(i, 0, inventory.enabled_items.size())
var k = inventory.enabled_items[id]
2025-07-16 08:32:40 +00:00
var container = prop_containers[i]
var button = container.get_child(0).get_child(0) as TextureButton
_display_texture_by_key(button, k)
if id == inventory.current_index:
2025-07-16 08:32:40 +00:00
container.get_child(0).add_child(select_mark)
prop_scroll.scroll_horizontal = PROP_CONTAINER_X
2025-07-16 08:32:40 +00:00
func _update_select_mark():
if not is_instance_valid(select_mark):
select_mark = TextureRect.new()
select_mark.custom_minimum_size = Vector2(PROP_CONTAINER_X, PROP_CONTAINER_X)
# select_mark
select_mark.texture = preload("uid://c0gjes4a8ou3b")
2025-07-16 08:32:40 +00:00
else:
var parent = select_mark.get_parent()
if parent:
parent.remove_child(select_mark)
func _display_texture_by_key(button: TextureButton, key: String) -> void:
if not key or not button:
if button:
button.texture_normal = null
return
if not items_dict.has(key):
return
var item = items_dict[key]
2025-07-09 13:12:28 +00:00
var texture = cached_inventory_textures.get(item.key)
if not texture:
if item.texture_path:
texture = load(item.texture_path) as Texture2D
if not texture:
printerr("PropHud _display_texture_by_key: No texture found for item:", item)
2025-07-16 08:32:40 +00:00
return
2025-07-09 13:12:28 +00:00
else:
cached_inventory_textures[item.key] = texture
button.texture_normal = texture
2025-07-16 08:32:40 +00:00
var t_size = texture.get_size()
var max_dimension = max(t_size.x, t_size.y)
var p_scale = min(PROP_CONTROL_X / t_size.x, PROP_CONTROL_X / t_size.y)
button.scale = Vector2(p_scale, p_scale)
2025-07-16 08:32:40 +00:00
button.size = Vector2(max_dimension, max_dimension)
func on_left_pressed() -> void:
2025-01-13 08:09:57 +00:00
if locked:
return
sfx_click.play()
2025-07-09 15:04:08 +00:00
inventory.index_wrap_add(-1)
_update_prop_display_with_texture()
_tween_container(true)
_mouse_moved_on_listening()
func on_right_pressed() -> void:
2025-01-13 08:09:57 +00:00
if locked:
return
sfx_click.play()
2025-07-09 15:04:08 +00:00
inventory.index_wrap_add(1)
_update_prop_display_with_texture()
_tween_container(false)
_mouse_moved_on_listening()
func _tween_container(left_to_right := true) -> void:
if container_tween and container_tween.is_running():
container_tween.kill()
2025-07-16 08:32:40 +00:00
container_tween = create_tween()
2025-07-16 08:32:40 +00:00
prop_scroll.scroll_horizontal = 2 * PROP_CONTAINER_X if left_to_right else 0.0
container_tween.tween_property(prop_scroll, "scroll_horizontal", PROP_CONTAINER_X, 0.2)
2025-07-16 08:32:40 +00:00
func _on_prop_gui_input(event: InputEvent, id: int) -> void:
if locked or not event is InputEventMouseButton:
return
2025-07-16 08:32:40 +00:00
var mouse_event = event as InputEventMouseButton
if not mouse_event.is_pressed():
return
2025-07-16 08:32:40 +00:00
match mouse_event.button_index:
MOUSE_BUTTON_LEFT:
_on_prop_pressed(id)
2025-07-16 08:32:40 +00:00
MOUSE_BUTTON_RIGHT:
_on_prop_inspected(id)
func _on_prop_inspected(id := 0) -> void:
if locked:
return
2025-07-16 08:32:40 +00:00
var prop_key = ""
2025-07-16 08:32:40 +00:00
if id >= 0 and id < inventory.enabled_items.size():
prop_key = inventory.enabled_items[id]
else:
prop_key = inventory.current_item_key()
2025-07-16 08:32:40 +00:00
if prop_key:
sfx_inspect.play()
print("[PropHud] inspect prop:", prop_key)
2025-07-09 04:44:13 +00:00
inspect_item(prop_key, false)
func _on_prop_pressed(id := 0) -> void:
2025-01-13 08:09:57 +00:00
if locked:
return
2025-07-16 08:32:40 +00:00
2025-01-13 08:09:57 +00:00
sfx_click.play()
if GlobalConfig.DEBUG:
print("PropHUD Panel pressed.")
2025-07-16 08:32:40 +00:00
focus_mode = FOCUS_ALL
grab_focus()
2025-07-16 08:32:40 +00:00
if id >= 0 and id < inventory.enabled_items.size():
inventory.current_index = id
2025-07-16 08:32:40 +00:00
_update_prop_display_with_texture()
_mouse_moved_on_listening(true)
func _toggle_scroll(fold := true) -> void:
if prop_blink and prop_blink.is_running():
prop_blink.kill()
if tween_scroll and tween_scroll.is_running():
tween_scroll.kill()
2025-07-16 08:32:40 +00:00
if fold:
# kill blink
select_mark.modulate.a = 1.0
selecting_bg.modulate.a = 0.0
# fold
tween_scroll = create_tween()
2025-07-16 08:32:40 +00:00
tween_scroll.tween_property(props_bag_scroll, "custom_minimum_size:x", 0.0, TWEEN_DURATION)
tween_scroll.parallel().tween_property(
2025-07-16 08:32:40 +00:00
hud_rect, "custom_minimum_size:x", PROP_CONTAINER_X + 10., TWEEN_DURATION
)
else:
# blink
if prop_containers.size() > 0:
prop_blink = create_tween().set_loops(0)
prop_blink.tween_property(select_mark, "modulate:a", 0.6, 1.).set_ease(
Tween.EASE_IN_OUT
)
prop_blink.parallel().tween_property(selecting_bg, "modulate:a", 0.2, 1.).set_ease(
Tween.EASE_IN_OUT
)
prop_blink.tween_property(select_mark, "modulate:a", 1., 1.).set_ease(Tween.EASE_IN_OUT)
prop_blink.parallel().tween_property(selecting_bg, "modulate:a", .6, 1.).set_ease(
Tween.EASE_IN_OUT
)
2025-07-16 08:32:40 +00:00
# unfold
var bag_x = prop_containers.size() * PROP_CONTAINER_X
var hud_x = bag_x + PROP_CONTAINER_X + 10.
tween_scroll = create_tween()
2025-07-16 08:32:40 +00:00
tween_scroll.tween_property(
props_bag_scroll, "custom_minimum_size:x", bag_x, TWEEN_DURATION
)
tween_scroll.parallel().tween_property(
hud_rect, "custom_minimum_size:x", hud_x, TWEEN_DURATION
)
func _on_mouse_entered() -> void:
2025-01-13 08:09:57 +00:00
if locked:
return
listen_mouse = true
_mouse_moved_on_listening()
func _on_mouse_exited() -> void:
listen_mouse = false
func _mouse_moved_on_listening(unfold_scroll := false) -> void:
if unfold_scroll:
_toggle_scroll(false)
2025-07-16 08:32:40 +00:00
if not displaying:
_toggle_details(true)
return
2025-07-16 08:32:40 +00:00
timer.start(display_time)
func _on_timer_timeout() -> void:
_toggle_details(false)
_toggle_scroll(true)
func _unhandled_input(event: InputEvent) -> void:
if locked or not listening_hotkey:
return
2025-07-16 08:32:40 +00:00
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()
elif event is InputEventMouseButton:
2025-07-16 08:32:40 +00:00
var mouse_event = event as InputEventMouseButton
if mouse_event.pressed:
match mouse_event.button_index:
MOUSE_BUTTON_WHEEL_DOWN:
on_left_pressed()
get_viewport().set_input_as_handled()
MOUSE_BUTTON_WHEEL_UP:
on_right_pressed()
get_viewport().set_input_as_handled()
2025-01-10 07:43:55 +00:00
func _input(event: InputEvent) -> void:
if locked:
return
2025-07-16 08:32:40 +00:00
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.parallel().tween_property(title_label, "modulate:a", 1.0, 0.3)
display_tween.parallel().tween_property(left_btn, "modulate:a", 1.0, 0.3)
display_tween.parallel().tween_property(right_btn, "modulate:a", 1.0, 0.3)
timer.start(display_time)
else:
displaying = false
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)
timer.stop()
func _toggle_btn_ability(v: bool) -> void:
left_btn.disabled = !v
right_btn.disabled = !v
2025-07-09 04:44:13 +00:00
func enable_important_item(prop_key: String, display_inspector: bool) -> void:
if not inventory or not prop_key:
return
2025-07-16 08:32:40 +00:00
2025-07-09 04:44:13 +00:00
if not items_dict.has(prop_key):
push_error("ImportantPropItem not found! key=" + prop_key)
return
2025-07-16 08:32:40 +00:00
2025-07-09 04:44:13 +00:00
inventory.enable_important_item(prop_key)
SceneManager.pop_notification("ui_notify_important_item_update")
2025-07-16 08:32:40 +00:00
2025-07-09 04:44:13 +00:00
if display_inspector:
2025-07-10 17:43:02 +00:00
sfx_inspect.play()
2025-07-09 04:44:13 +00:00
inspect_item(prop_key, true, true)
func enable_prop_item(prop_key: String, inspect := true) -> void:
if not inventory or not prop_key:
2025-07-09 04:44:13 +00:00
printerr("PropHUD Enable prop item: No inventory or prop_key provided.")
return
2025-07-16 08:32:40 +00:00
if not items_dict.has(prop_key):
push_error("PropItem not found! key=" + prop_key)
return
2025-07-16 08:32:40 +00:00
inventory.enable_item(prop_key)
2025-07-09 13:12:28 +00:00
_reload_cache_and_realign_display()
2025-07-16 08:32:40 +00:00
if GlobalConfig.DEBUG:
print("PropHUD Enable prop item:", prop_key)
2025-07-16 08:32:40 +00:00
if inspect:
sfx_new_prop.play()
inspect_item(prop_key)
2025-07-09 04:44:13 +00:00
func inspect_item(prop_key: String, display_obtained := true, as_important_item := false):
var inspector = SceneManager.get_inspector() as PropInspector
2025-07-16 08:32:40 +00:00
if not inspector or not items_dict.has(prop_key):
return
var item = items_dict[prop_key]
var texture: Texture2D = null
# 检查是否有独立的 inspect 图片
if item.inspect_path:
texture = load(item.inspect_path) as Texture2D
else:
texture = cached_inventory_textures.get(prop_key)
if not texture and item.texture_path:
texture = load(item.texture_path) as Texture2D
if texture:
cached_inventory_textures[prop_key] = texture
if not texture:
printerr("prophud inspect_item invalid texture for key:", prop_key)
return
inspector.pop_prop_inspection(
prop_key, texture, display_obtained, as_important_item
2025-07-16 08:32:40 +00:00
)
func disable_prop_item(prop_key: String) -> void:
if not inventory or not prop_key:
return
inventory.disable_item(prop_key)
2025-07-09 13:12:28 +00:00
_reload_cache_and_realign_display()
2025-01-13 08:09:57 +00:00
func _align_container_size() -> void:
2025-07-16 08:32:40 +00:00
if not inventory:
return
# 添加新容器
while inventory.enabled_items.size() > prop_containers.size():
append_prop_container()
2025-07-16 08:32:40 +00:00
# 删除多余容器
while inventory.enabled_items.size() < prop_containers.size():
remove_prop_container()
2025-07-16 08:32:40 +00:00
if displaying:
_mouse_moved_on_listening()
# 如果正在展示,则更新 scroll 长度
2025-07-16 08:32:40 +00:00
if props_bag_scroll.custom_minimum_size.x > 0.0:
_toggle_scroll(false)
func append_prop_container() -> void:
var container = CenterContainer.new()
container.custom_minimum_size = Vector2(PROP_CONTAINER_X, PROP_CONTAINER_X)
container.set_anchors_preset(Control.PRESET_FULL_RECT)
2025-07-16 08:32:40 +00:00
var control = Control.new()
control.custom_minimum_size = Vector2(PROP_CONTROL_X, PROP_CONTROL_X)
control.set_anchors_preset(Control.PRESET_FULL_RECT)
container.add_child(control)
2025-07-16 08:32:40 +00:00
var prop = TextureButton.new()
prop.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED
control.add_child(prop)
2025-07-16 08:32:40 +00:00
props_bag.add_child(container)
2025-07-16 08:32:40 +00:00
prop_containers.append(container)
var index = prop_containers.size() - 1
prop.gui_input.connect(_on_prop_gui_input.bind(index))
func remove_prop_container() -> void:
2025-07-16 08:32:40 +00:00
if prop_containers.is_empty():
return
2025-07-16 08:32:40 +00:00
var container = prop_containers.pop_back()
if container and is_instance_valid(container):
props_bag.remove_child(container)
container.queue_free()
2025-01-13 08:09:57 +00:00
2025-01-13 08:09:57 +00:00
func on_toggle_invalid_prop():
if not inventory or not inventory.current_item_key():
return
2025-07-16 08:32:40 +00:00
2025-05-14 20:43:55 +00:00
if GlobalConfig.DEBUG:
print("使用无效道具. invalid_prop shake. current prop:", inventory.current_item_key())
2025-07-16 08:32:40 +00:00
if shake_tween and shake_tween.is_running():
2025-01-13 08:09:57 +00:00
shake_tween.kill()
2025-07-16 08:32:40 +00:00
# 抖动效果
2025-01-13 08:09:57 +00:00
shake_tween = create_tween()
2025-07-16 08:32:40 +00:00
var count = int(SHAKE_DURATION * SHAKE_FPS)
var delta_t = 1.0 / SHAKE_FPS
2025-01-13 08:09:57 +00:00
var origin_pos = Vector2.ZERO
2025-05-14 20:43:55 +00:00
var prop = display_prop
2025-07-16 08:32:40 +00:00
2025-01-13 08:09:57 +00:00
prop.modulate = Color(1.0, 0.6, 0.6, 1.0)
2025-07-16 08:32:40 +00:00
2025-01-13 08:09:57 +00:00
for i in range(count):
2025-07-16 08:32:40 +00:00
var decay = exp(-i)
var offset = Vector2(
randf_range(-SHAKE_DELTA, SHAKE_DELTA) * decay,
randf_range(-SHAKE_DELTA, SHAKE_DELTA) * decay
)
2025-01-13 08:09:57 +00:00
shake_tween.tween_property(prop, "position", origin_pos + offset, delta_t).set_trans(
Tween.TRANS_CUBIC
)
2025-07-16 08:32:40 +00:00
2025-01-13 08:09:57 +00:00
shake_tween.tween_callback(_reset_prop_modulate)
func _reset_prop_modulate():
2025-07-16 08:32:40 +00:00
if display_prop:
display_prop.modulate = Color.WHITE