xiandie/scene/entity/ux/sign_snapper.gd

100 lines
2.8 KiB
GDScript3
Raw Normal View History

@tool
extends Marker2D
class_name SignSnapper
signal arrived
@export var enabled := true
# 仅使用 x 坐标;否则也用 y 进行 walk to
@export var use_x_only := true
# 允许小幅调动0-3px 被忽视3-5px 被视作 5px
@export var auto_adjust := true
# x 左右有效范围
@export_range(0.0, 20.0, 0.1) var radius := 0.0
# 让玩家走到边缘,特别适用于 npc 对话等情景
@export var walk_to_edge := false
@export var action_on_arrived := 3
@export_tool_button("debug 检查玩家触发位置") var debug_check_player_pos = _debug_check_player_pos
var detacted_sign: Sign
# arrived 延时: delay_arrived-arrived
static var delay_arrived := 0.0
static var delay_after_action := 0.0
# 玩家在和 sign 交互时,如果读到节点中有 SignSnapper则移动到该位置(x 对齐)
func _ready():
if not get_parent():
return
for c in get_parent().get_children():
if c is Sign:
detacted_sign = c
break
if not detacted_sign and Engine.is_editor_hint():
printerr("SignSnapper must have a Sign sibling node!")
if not Engine.is_editor_hint():
detacted_sign.interacted.connect(_on_interacted)
func _on_interacted():
if not detacted_sign or not enabled:
return
var player = SceneManager.get_player()
var target_pos = global_position
if use_x_only:
target_pos.y = player.global_position.y
var player_x = player.global_position.x
if (radius > 0 and absf(target_pos.x - player_x) > radius) or walk_to_edge:
# clamp in range
if target_pos.x > player_x:
target_pos.x -= radius
else:
target_pos.x += radius
var diff = absf(player_x - target_pos.x)
if auto_adjust:
# 允许小幅调动0-3px 被忽视3-5px 被视作 5px
if diff < 3:
target_pos.x = player_x
diff = 0
elif diff < 5:
target_pos.x = player_x + (5 if target_pos.x > player_x else -5)
diff = 5
var tween
if diff == 0:
tween = create_tween()
else:
tween = player.walk_to(target_pos)
if delay_arrived > 0:
tween.tween_interval(delay_arrived)
if action_on_arrived != 3:
tween.tween_callback(SceneManager.player_action.bind(action_on_arrived))
if delay_after_action > 0:
tween.tween_interval(delay_after_action)
tween.tween_callback(arrived.emit)
func _debug_check_player_pos():
2025-06-25 20:39:02 +00:00
if not Engine.is_editor_hint():
return
var grounds = get_tree().get_nodes_in_group("ground")
if not grounds:
printerr("no ground")
return
var ground = grounds[0]
if use_x_only:
ground.replace_player_to_portal = true
var player = ground.player
if radius > 0:
var player_x = player.global_position.x
if absf(player_x - global_position.x) > radius:
# clamp in range
if player_x < global_position.x:
player.global_position.x = global_position.x - radius
else:
player.global_position.x = global_position.x + radius
else:
player.global_position.x = global_position.x
if not use_x_only:
player.global_position.y = global_position.y