extends Node const TARGET_RANGE := 20.0 var mouse_press_pos: Vector2 = Vector2.ZERO @onready var player: CharacterBody3D = get_parent() @onready var camera: Camera3D = get_parent().get_node("CameraPivot/Camera3D") func _unhandled_input(event: InputEvent) -> void: if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: mouse_press_pos = event.position else: var drag: float = event.position.distance_to(mouse_press_pos) if drag < 5.0: _try_target_under_mouse(event.position) if event.is_action_pressed("target_next"): _cycle_target() func _try_target_under_mouse(mouse_pos: Vector2) -> void: var from := camera.project_ray_origin(mouse_pos) var to := from + camera.project_ray_normal(mouse_pos) * TARGET_RANGE var space := player.get_world_3d().direct_space_state var query := PhysicsRayQueryParameters3D.create(from, to) query.collision_mask = 4 query.collide_with_areas = true query.collide_with_bodies = false var result := space.intersect_ray(query) if result: var hit_target := result.collider.get_parent() as Node3D EventBus.target_requested.emit(player, hit_target) else: EventBus.target_requested.emit(player, null) func _cycle_target() -> void: var targets := get_tree().get_nodes_in_group("enemies") + get_tree().get_nodes_in_group("portals") if targets.is_empty(): EventBus.target_requested.emit(player, null) return var current: Node3D = PlayerData.target if current == null or current not in targets: EventBus.target_requested.emit(player, targets[0]) return var idx := targets.find(current) var next_idx := (idx + 1) % targets.size() EventBus.target_requested.emit(player, targets[next_idx])