126 lines
3.2 KiB
GDScript
126 lines
3.2 KiB
GDScript
extends Control
|
|
|
|
## A draggable modifier piece. Follows the cursor directly — no ghost, no
|
|
## Godot drag-and-drop API (which fights with 3D raycasting).
|
|
##
|
|
## While dragging it asks the DropCatcher which slot is under the cursor, so
|
|
## the player can SEE what they're about to hit before committing.
|
|
|
|
@export var paint_texture: Texture2D:
|
|
set(value):
|
|
paint_texture = value
|
|
queue_redraw()
|
|
|
|
@export_range(0.0, 1.0) var top_width_ratio := 0.55
|
|
|
|
# Injected by the tray on spawn.
|
|
var drop_catcher: Control
|
|
var tray: Node
|
|
var modifier: Modifier
|
|
|
|
var _dragging := false
|
|
var _home_position := Vector2.ZERO
|
|
var _drag_offset := Vector2.ZERO
|
|
var _hovered_slot: StringName = &""
|
|
|
|
|
|
func _ready() -> void:
|
|
mouse_filter = Control.MOUSE_FILTER_STOP
|
|
if custom_minimum_size == Vector2.ZERO:
|
|
custom_minimum_size = Vector2(90, 110)
|
|
size = custom_minimum_size
|
|
|
|
|
|
func _draw() -> void:
|
|
var w := size.x
|
|
var h := size.y
|
|
var inset := w * (1.0 - top_width_ratio) * 0.5
|
|
|
|
var points := PackedVector2Array([
|
|
Vector2(inset, 0), Vector2(w - inset, 0),
|
|
Vector2(w, h), Vector2(0, h),
|
|
])
|
|
|
|
if paint_texture != null:
|
|
var uvs := PackedVector2Array([
|
|
Vector2(inset / w, 0), Vector2((w - inset) / w, 0),
|
|
Vector2(1, 1), Vector2(0, 1),
|
|
])
|
|
var white := PackedColorArray([Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE])
|
|
draw_polygon(points, white, uvs, paint_texture)
|
|
else:
|
|
draw_colored_polygon(points, Color(0.8, 0.8, 0.8))
|
|
|
|
# Outline turns green when hovering a valid slot — the player's only cue
|
|
# that the drop will land.
|
|
var outline_col := Color(0, 0, 0, 0.6)
|
|
var outline_w := 2.0
|
|
if _dragging and _hovered_slot != &"":
|
|
outline_col = Color(0.3, 1.0, 0.4, 0.9)
|
|
outline_w = 4.0
|
|
|
|
var outline := points
|
|
outline.append(points[0])
|
|
draw_polyline(outline, outline_col, outline_w)
|
|
|
|
|
|
func _gui_input(event: InputEvent) -> void:
|
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
|
|
if event.pressed:
|
|
_start_drag()
|
|
else:
|
|
_end_drag()
|
|
|
|
|
|
func _start_drag() -> void:
|
|
_dragging = true
|
|
_home_position = global_position
|
|
_drag_offset = global_position - get_global_mouse_position()
|
|
|
|
# top_level lets us move by global_position without reparenting.
|
|
# Reparenting mid-drag breaks the release event.
|
|
top_level = true
|
|
z_index = 100
|
|
modulate.a = 0.85
|
|
|
|
|
|
func _end_drag() -> void:
|
|
if not _dragging:
|
|
return
|
|
|
|
_dragging = false
|
|
_hovered_slot = &""
|
|
modulate.a = 1.0
|
|
z_index = 0
|
|
|
|
var applied := false
|
|
if drop_catcher == null:
|
|
push_error("SectorSwatch has no drop_catcher.")
|
|
else:
|
|
applied = drop_catcher.apply_modifier_at_screen_pos(
|
|
get_global_mouse_position(), modifier
|
|
)
|
|
|
|
# Always snap home. The tray decides whether to consume the piece.
|
|
top_level = false
|
|
global_position = _home_position
|
|
queue_redraw()
|
|
|
|
if applied and tray != null and tray.has_method(&"on_piece_used"):
|
|
tray.on_piece_used(self)
|
|
|
|
|
|
func _process(_delta: float) -> void:
|
|
if not _dragging:
|
|
return
|
|
|
|
var mouse := get_global_mouse_position()
|
|
global_position = mouse + _drag_offset
|
|
|
|
# Live hover feedback — which slot would this land on right now?
|
|
if drop_catcher != null and drop_catcher.has_method(&"hover"):
|
|
var slot: StringName = drop_catcher.hover(mouse)
|
|
if slot != _hovered_slot:
|
|
_hovered_slot = slot
|
|
queue_redraw()
|