signalis-eb/godot/src/interactor.gd
2025-06-28 10:30:12 -04:00

72 lines
2.1 KiB
GDScript

class_name Interactor extends Node3D
const EnterSignals: Array[String] = ["area_entered", "body_entered"]
const ExitSignals: Array[String] = ["area_exited", "body_exited"]
@export var area: Area3D
@export var root_node: Node = self
class InteractableNode:
var _body: CollisionObject3D
var _interactable: Interactable
var global_position: Vector3:
get: return _body.global_position
func _init(body: CollisionObject3D, interactable: Interactable):
_body = body
_interactable = interactable
func interact(interactor: Node):
_interactable.interact(interactor)
func squared_distance_to(b: InteractableNode) -> float:
return global_position.distance_squared_to(b.global_position)
var interactables: Array[InteractableNode] = []
var _sorted: bool = false
func _ready() -> void:
_connect_many(area, EnterSignals, _on_object_entered)
_connect_many(area, ExitSignals, _on_object_exited)
func _connect_many(node: Node, signals: Array[String], fn: Callable):
for signal_name in signals:
node.connect(signal_name, fn)
func _on_object_entered(object: CollisionObject3D):
_sorted = false
var child = NodeExt.find_child_variant(object, Interactable)
if child.is_some():
interactables.append(InteractableNode.new(object, child.unwrap() as Interactable))
func is_same_object(object: CollisionObject3D, node: InteractableNode) -> bool:
return object == node._body
func _on_object_exited(object: CollisionObject3D):
var index = interactables.find_custom(
func(node): return is_same_object(object, node)
)
if index >= 0:
interactables.remove_at(index)
func _distance_from(node: InteractableNode) -> float:
return global_position.distance_squared_to(node.global_position)
func _distance_compare(a: InteractableNode, b: InteractableNode):
return _distance_from(a) > _distance_from(b)
func _sort_by_distance():
interactables.sort_custom(_distance_compare)
_sorted = true
func interact(interactable: InteractableNode):
interactable.interact(root_node)
func interact_nearest():
if not _sorted:
_sort_by_distance()
if len(interactables) > 0:
return interact(interactables[0])