tli/scripts/globals/selection_manager.gd
2024-10-13 21:07:35 +10:00

122 lines
2.9 KiB
GDScript

extends Node
## Handles selection of units.
const MIN_DRAG_DISTANCE: float = 15
const UNIT_SELECT_OFFSET: float = 0.25
const ANIM_MAX_STEP = 30
const MIN_VISIBLE_UNITS = 25
const MAX_VISIBLE_UNITS = 120
var selecting: bool = false
var advance_anim_step: int = 1
var _visible_units: Dictionary = {}
var _mouse_pressed: bool = false
var _selection_rect: Rect2 = Rect2()
var _rect_style := preload("res://resources/styles/selection_rect.tres")
@onready var camera: Camera3D = StaticNodesManager.main_camera
@onready var rect_panel: Panel = Panel.new()
func _ready() -> void:
rect_panel.visible = false
rect_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
rect_panel.add_theme_stylebox_override("panel", _rect_style)
add_child(rect_panel)
func _process(_delta: float) -> void:
selecting = (
_mouse_pressed
and _selection_rect.size.length() >= MIN_DRAG_DISTANCE
)
_handle_selection_box()
_handle_unit_selection()
_handle_advance_anim_step()
func _input(event: InputEvent) -> void:
if event is InputEventMouseButton:
var button_event := event as InputEventMouseButton
if button_event.button_index == MOUSE_BUTTON_LEFT:
_mouse_pressed = button_event.pressed
if _mouse_pressed:
_selection_rect.position = button_event.position
_selection_rect.size = Vector2.ZERO
elif selecting:
_set_selection_state(false)
if event is InputEventMouseMotion:
if _mouse_pressed:
var mouse_pos := (event as InputEventMouseMotion).position
_selection_rect.size = mouse_pos - _selection_rect.position
func add_unit_to_visible(unit: Unit) -> void:
var unit_id := unit.get_instance_id()
if _visible_units.keys().has(unit_id):
return
_visible_units[unit_id] = unit as Unit
func remove_unit_from_visible(unit: Unit) -> void:
var unit_id := unit.get_instance_id()
if not _visible_units.keys().has(unit_id):
return
_visible_units.erase(unit_id)
func _handle_advance_anim_step() -> void:
var remapped_unclamped := remap(
_visible_units.size(),
MIN_VISIBLE_UNITS,
MAX_VISIBLE_UNITS,
0,
1,
)
var clamped := clampf(remapped_unclamped, 0, 1)
advance_anim_step = roundi(lerpf(1, ANIM_MAX_STEP, clamped))
func _handle_selection_box() -> void:
rect_panel.visible = selecting
if not selecting:
return
var rect_abs := _selection_rect.abs()
rect_panel.position = rect_abs.position
rect_panel.size = rect_abs.size
func _handle_unit_selection() -> void:
if not selecting:
return
_set_selection_state(true)
func _set_selection_state(hover: bool) -> void:
var rect_abs := _selection_rect.abs()
for unit: Unit in _visible_units.values():
if unit is not ControlledUnit:
continue
var controlled_unit := unit as ControlledUnit
var point := camera.unproject_position(
unit.global_position
+ (Vector3.UP * UNIT_SELECT_OFFSET)
)
if hover:
controlled_unit.set_hovered_rect(rect_abs.has_point(point))
else:
controlled_unit.set_selected(rect_abs.has_point(point))
controlled_unit.set_hovered_rect(false)