hadean-godot/Scripts/GridInput.gd

214 lines
5.9 KiB
GDScript

extends Node2D
@onready var grid: Grid = $/root/Root/Grid
@export var highlight_color: Color = Color(1, 1, 1, 0.2)
@export var deletion_color: Color = Color(1, 1, 1, 0.2)
var animation_speed = 0.8
enum AnimationState {
Active,
Fading,
Exploding
}
enum CursorMode {
Selection,
Deletion
}
class Display:
var mode: CursorMode
var box: Rect2
var desired_box: Rect2
var opacity: float
var confirmed_callback: Callable
var cancelled_callback: Callable
var dragging: bool = false
var drag_from: Rect2
var state: AnimationState = AnimationState.Fading
var display_count = 10
var displays: Array[Display] = []
var display_idx = -1
var display: Display:
get():
return displays[display_idx] if display_idx >= 0 else null
func new_display():
if display_idx < 0:
display_idx += display_count
display_idx += 1
display_idx = display_idx % display_count
display.dragging = false
display.box = get_viewport_rect()
display.box.size = Vector2.ONE * max(display.box.size.x, display.box.size.y)
display.box = Rect2(display.box.position + display.box.size / -2.0, display.box.size).grow(100)
display.opacity = -2.0
display.state = AnimationState.Active
display.desired_box = display.box
func end_display():
if display == null: return
if display.state == AnimationState.Active:
display.state = AnimationState.Fading
if display.state == AnimationState.Exploding:
display.desired_box = display.desired_box.grow(30)
display_idx -= display_count
var left_dragging = false
var left_mouse_down = false
var left_mouse_down_start_pos: Vector2 = Vector2.ZERO
func _is_mouse_pos_significant() -> bool:
return (get_local_mouse_position() - left_mouse_down_start_pos).length_squared() > 50
#eventually, replace this weird shit with an array of old rects that are fading out
#let them fade out, and just remove them. have a method to abandon the current box
#and start a new one that just follows the normal box-booting procedure.
#if you're not in the grid, it simply wont be created yet. have to have a has_box prop
#or something like that. but this is good enough for now
func _ready():
selection_manager = Util.find(SelectionManager)
displays.resize(display_count)
for idx in range(displays.size()):
displays[idx] = Display.new()
displays[idx].opacity = 0.0
displays[idx].state = AnimationState.Fading
mouse_in_grid = grid.contains_world_pos(get_local_mouse_position())
if mouse_in_grid:
new_display()
func _lerp_opacity(c: Color, t: float) -> Color:
return Color(c.r, c.g, c.b, c.a * t)
func update_display_animations(delta):
for d in displays:
var old_size = d.box.size
var desired_size = d.desired_box.size
var new_size = 0.0
match d.state:
AnimationState.Exploding:
new_size = lerp(old_size, desired_size, delta * 3 * animation_speed)
AnimationState.Active, \
AnimationState.Fading:
if old_size.length_squared() > desired_size.length_squared():
new_size = lerp(old_size, desired_size, delta * 30 * animation_speed)
else:
new_size = lerp(old_size, desired_size, delta * 30 * animation_speed)
var old_center = d.box.get_center()
var desired_center = d.desired_box.get_center()
var new_center = lerp(old_center, desired_center, delta * 50 * animation_speed)
d.box = Rect2(new_center - new_size / 2.0, new_size)
match d.state:
AnimationState.Fading:
d.opacity = lerp(d.opacity, 0.0, delta * 10 * animation_speed)
AnimationState.Active:
d.opacity = lerp(d.opacity, 1.0, delta * 20 * animation_speed)
AnimationState.Exploding:
d.opacity = lerp(d.opacity, -1.0, delta * 5 * animation_speed)
var mouse_in_grid: bool = false
var mousing_over: Rect2
func _process(delta):
queue_redraw()
var mouse = get_local_mouse_position()
var new_mousing_over = grid.quantize_on_objects(mouse)
var new_mouse_in_grid = grid.contains_world_pos(mouse)
if mouse_in_grid && !new_mouse_in_grid:
end_display()
if !mouse_in_grid && new_mouse_in_grid:
new_display()
display.mode = CursorMode.Deletion
if new_mousing_over != mousing_over && display == null && new_mouse_in_grid:
new_display()
mouse_in_grid = new_mouse_in_grid
mousing_over = new_mousing_over
update_display_animations(delta)
# only do anything new if we have a mouse on the grid!
if !mouse_in_grid: return
if display == null: return
display.desired_box = mousing_over
func _draw():
#draw_rect(mousing_over.grow(-8), Color.FIREBRICK, true)
for d in displays:
match d.mode:
CursorMode.Selection:
draw_rect(d.box, _lerp_opacity(highlight_color, d.opacity), true)
CursorMode.Deletion:
draw_rect(d.box, _lerp_opacity(deletion_color, d.opacity), true)
enum State {
# Selection will automatically go do selection things
Selection,
# whereas these hoes will just call callbacks.
LineDrag,
Area,
}
var state: State = State.Selection
var selection_manager: SelectionManager
func _begin_drag(pos: Vector2):
display.drag_from
print("drag from", pos)
func _dragging(pos: Vector2):
pass
func _end_drag(pos: Vector2):
print("to", pos)
func _click(pos: Vector2):
print("click at", pos)
if display == null:
new_display()
display.state = AnimationState.Exploding
end_display()
var tile = grid._get_tile_at_world_pos(pos)
if tile == null:
selection_manager.clear()
return
selection_manager.select_array([tile])
func _double_click(pos: Vector2):
pass
func _right_click(pos: Vector2):
pass
func _input(event: InputEvent):
if event is InputEventMouseMotion:
if left_mouse_down:
if not left_dragging and _is_mouse_pos_significant():
left_dragging = true
_begin_drag(get_local_mouse_position())
elif left_dragging:
_dragging(get_local_mouse_position())
elif event is InputEventMouseButton:
if event.button_index == 1:
left_mouse_down = event.pressed
if event.pressed:
left_mouse_down_start_pos = get_local_mouse_position()
else:
if left_dragging:
left_dragging = false
_end_drag(get_local_mouse_position())
else:
_click(get_local_mouse_position())