72 lines
2.1 KiB
GDScript
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])
|