xiandie/scene/entity/ux/sign.gd

167 lines
4.6 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
class_name Sign extends Control
@export var show_sign := true:
set(val):
show_sign = val
# 隐藏时强制设置透明度为 0显示时需要根据激活状态设置透明度
if not val:
modulate.a = 0.0
elif activated:
modulate.a = 1.0
@export var draw_shadow := false
signal interacted
signal cancel
@onready var texture_container = %TextureContainer as Container
@onready var sprite2d = %Sprite2D as Sprite2D
# var shadow_texture = preload("res://asset/art/ui/action_mark/探索空心ui.png") as Texture2D
# 同时只能有一个物品被激活交互态,其他物品进入等待队列
static var occupied: NodePath
static var _pending_activate_sign := [] as Array[NodePath]
# 使用互斥锁保证线程安全。在操作 occupied 或 _pending_activate_sign 时需要先锁定,操作完成后解锁
static var mutex = Mutex.new()
var activated = false:
set(val):
activated = val
queue_redraw()
# var sprite2d = Sprite2D.new()
var base_scale = Vector2.ONE
func _ready() -> void:
base_scale = scale
# layer = GlobalConfig.CANVAS_LAYER_FG
var point_light = get_node_or_null("../PointLight2D")
if point_light:
point_light.energy = 0.0
var area2d = get_node_or_null("../Area2D")
if area2d:
area2d.body_entered.connect(activate)
area2d.body_exited.connect(disactivate)
if not Engine.is_editor_hint():
modulate.a = 0
func activate(_body: Node2D) -> void:
# point_light.energy = 1.0
if not is_node_ready():
await ready
var path := get_path()
mutex.lock()
if occupied and occupied != path:
_pending_activate_sign.append(path)
mutex.unlock()
return
else:
occupied = path
activated = true
mutex.unlock()
if activated and show_sign:
var tween = create_tween()
tween.tween_property(self, "modulate:a", 1.0, 0.2)
var p_tween = tween.parallel()
p_tween.tween_property(self, "scale", base_scale * Vector2(1.2, 1.2), 0.3)
p_tween.tween_property(self, "scale", base_scale, 0.1)
# if activated:
# focus_mode = FOCUS_ALL
# grab_focus()
# 绘制 prop 图标
# func _draw() -> void:
# if draw_shadow and sprite2d.texture:
# var texture_x = sprite2d.texture.get_size().x
# # var radius = max(36.0, min(texture_x * 0.5, 48.0))
# var radius = 46.0
# var rect = Rect2(-radius, -radius, radius * 2, radius * 2)
# draw_texture_rect(shadow_texture, rect, false)
# # 比 2.0 略小,选择使用 1.6
# var texture_scale = min(radius * 1.6 / texture_x, 1.1)
# sprite2d.scale = Vector2(texture_scale, texture_scale)
# # var texture_radius = shadow_texture.x * 0.5
# # draw_circle(Vector2.ZERO, radius, Color(0.8, 0.8, 0.8, 0.8), false, 6.0, true)
# # radius -= 6.0
# # if radius > 0:
# # draw_circle(Vector2.ZERO, radius, Color(0.3, 0.3, 0.3, 0.3))
# else:
# sprite2d.scale = Vector2.ONE
func disactivate(_body: Node2D) -> void:
# release_focus()d
mutex.lock()
if activated:
activated = false
occupied = ""
while _pending_activate_sign.size() > 0:
var path = _pending_activate_sign.pop_front()
var _sign = get_node_or_null(path)
if _sign:
_sign.activate(null)
break
else:
# make sure the sign is not in the pending list
_pending_activate_sign.erase(get_path())
# double check. because the sign may be activated by other body
if activated:
disactivate(_body)
mutex.unlock()
# point_light.energy = 0.0
if show_sign:
create_tween().tween_property(self, "modulate:a", 0.0, 0.2)
func _unhandled_input(event: InputEvent) -> void:
if activated:
if event.is_action_pressed("interact"):
interacted.emit()
if is_inside_tree():
# grab focus 放在 emit 后面,避免在 emit 时 prop hud 失去 focus
# 传送时会导致 is_inside_tree 为 false此时也无需与 prop hud 抢占 focus
focus_mode = FOCUS_ALL
grab_focus()
_set_handled()
elif event.is_action_pressed("cancel"):
cancel.emit()
_set_handled()
release_focus()
func _set_handled():
var viewport = get_viewport()
if viewport:
viewport.set_input_as_handled()
var shake_tween
# 使用无效道具,抖动提示
func invalid_shake():
if shake_tween:
shake_tween.kill()
# 抖动效果,逐渐减弱
shake_tween = create_tween()
var fps := 12.0
var duration := 0.7
var delta := 4.0
var origin_pos = Vector2.ZERO
var count = int(duration * fps)
var delta_t = 1.0 / fps
sprite2d.modulate = Color(1.0, 0.6, 0.6, 1.0)
for i in range(count):
var offset = Vector2(randf_range(-delta, delta), randf_range(-delta, delta)) * exp(-i)
shake_tween.tween_property(sprite2d, "position", origin_pos + offset, delta_t).set_trans(
Tween.TRANS_CUBIC
)
shake_tween.tween_callback(_reset_modulate)
func _reset_modulate():
sprite2d.modulate = Color(1.0, 1.0, 1.0, 1.0)