import { Area3D, Callable, Node, NodePath, Variant } from 'godot' import Interactable from './interactable' import { export_ } from 'godot.annotations' class InteractableDistance { static Empty = new InteractableDistance(Infinity) interactable?: Interactable distance: number constructor(distance: number, interactable?: Interactable) { this.distance = distance this.interactable = interactable } } export default class Interactor extends Area3D { @export_(Variant.Type.TYPE_NODE_PATH) private _root_node: NodePath = new NodePath('.') private _root!: Node get root_node() { return this._root } _interactables: Array = [] _ready(): void { this._root = this.get_node(this._root_node) this.area_entered.connect(Callable.create( this, this._on_area_entered )) this.area_exited.connect(Callable.create( this, this._on_area_exited )) } _on_area_entered(area: Area3D) { if (area instanceof Interactable) { this._interactables.push(area) } } _on_area_exited(area: Area3D) { const index = this._interactables.indexOf(area as Interactable) if (index >= 0) { this._interactables.splice(index, 1) } } try_interact_nearest() { // @ts-ignore const nearest = this._interactables.reduce(this.find_nearest.bind(this), InteractableDistance.Empty) if (nearest !== InteractableDistance.Empty && nearest.interactable != null) { nearest.interactable.interact(this) } } find_nearest(nearest: InteractableDistance, target: Interactable): InteractableDistance { const distance = this.global_position.distance_to(target.global_position) if (distance < nearest.distance) { return new InteractableDistance(distance, target) } else { return nearest } } }