Godot-RTS-Template/core/scripts/player_selection_manager.gd
2025-07-28 15:55:03 +02:00

129 lines
4.2 KiB
GDScript

extends Control
class_name SelectionManager
var draw_selection := false
var drag_start: Vector2
var select_box: Rect2
var perform_selection := false # Flag to trigger selection in _physics_process
@onready var box_color: Color = ProjectSettings.get_setting("game/interface/selection_rectangle_color", Color("#00ff0066"))
@onready var box_color_outline: Color = ProjectSettings.get_setting("game/interface/selection_rectangle_color_outline", Color("#00ff00"))
@onready var optimized_rect: bool = ProjectSettings.get_setting("game/interface/optimized_rectangle", false)
@export var rect_style: StyleBoxFlat
@onready var cam = get_viewport().get_camera_3d()
@onready var hovercontrol:Button = $"../Button"
func _ready() -> void:
if optimized_rect:return
func _unhandled_input(e: InputEvent) -> void:
if e is InputEventMouseButton and e.button_index == MOUSE_BUTTON_LEFT:
if e.pressed:
draw_selection = true
drag_start = e.position
else:
# When button is released
draw_selection = false
if drag_start.is_equal_approx(e.position):
# Single click: set a zero-sized selection box
select_box = Rect2(e.position, Vector2.ZERO)
print("one click")
else:
# Drag: calculate the final selection box
var x_min = min(drag_start.x, e.position.x)
var y_min = min(drag_start.y, e.position.y)
select_box = Rect2(x_min, y_min,
max(drag_start.x, e.position.x) - x_min,
max(drag_start.y, e.position.y) - y_min)
perform_selection = true # Trigger selection in _physics_process
queue_redraw()
elif draw_selection and e is InputEventMouseMotion:
# Update selection box for drawing during drag
var x_min = min(drag_start.x, e.position.x)
var y_min = min(drag_start.y, e.position.y)
select_box = Rect2(x_min, y_min,
max(drag_start.x, e.position.x) - x_min,
max(drag_start.y, e.position.y) - y_min)
queue_redraw()
if !draw_selection and e is InputEventMouseMotion:
check_hover()
func _physics_process(_delta: float) -> void:
if perform_selection:
update_selected_units()
perform_selection = false
func check_hover():
# TODO: Hovering and display names
var selected = get_selected()
if selected.size() > 0:
hovercontrol.text = selected[0].name
else:
hovercontrol.text = ""
hovercontrol.queue_redraw()
pass
func get_selected() -> Array[Entity]:
var selected: Array[Node] = get_tree().get_nodes_in_group('selected-by-'+str(get_tree().get_multiplayer().get_unique_id()))
var selected_entity: Array[Entity]
selected_entity.assign(selected)
return selected_entity
## [param collision_mask] is 32768 by default, so the collision channel 16
func check_raycast(mouse_pos: Vector2, collision_mask: int = 32768) -> Dictionary:
var from = cam.project_ray_origin(mouse_pos)
var to = from + cam.project_ray_normal(mouse_pos) * 1000 # Magic number
var query = PhysicsRayQueryParameters3D.create(from,to,collision_mask)
query.collide_with_areas = true
query.collide_with_bodies = false
var space_state = cam.get_world_3d().direct_space_state
#DebugTools.DrawLine(from,to,25.0)
return space_state.intersect_ray(query)
func deselect_all() -> void:
var selected = get_selected()
for entity:Entity in selected:
entity.deselect()
func update_selected_units() -> void:
var units_array = get_tree().get_nodes_in_group("selectable-entity")
var raycast = {}
# Only perform raycast if it's a single click (zero-sized select_box)
if select_box.size == Vector2.ZERO:
raycast = check_raycast(select_box.position,)
if raycast and raycast.collider:
# select entity under mouse
# maybe can be extracted from hover instead?
var entity = raycast.collider.get_parent()
deselect_all()
entity.select()
# BUG: Deselect previous selection
return
for entity: Entity in units_array:
# TODO: Optimize by searching only in spawned chunks
# select entities within the selection/draggin box
if entity.is_in_selection(select_box, cam):
entity.select()
else:
entity.deselect()
func _draw() -> void:
if not (draw_selection or perform_selection):return
if select_box and rect_style:
if optimized_rect:
draw_rect(select_box,box_color)
draw_rect(select_box,box_color_outline,false,2.0,true)
elif rect_style:
draw_style_box(rect_style,select_box)