feat: nearest card selection
This commit is contained in:
parent
1939e49a91
commit
46f7463267
|
|
@ -73,4 +73,3 @@ func unload():
|
||||||
## Override in subclasses to add custom scene preparation logic
|
## Override in subclasses to add custom scene preparation logic
|
||||||
func prepare_scene_start(_scene_id: Scenes.id, _is_repeating: bool) -> void:
|
func prepare_scene_start(_scene_id: Scenes.id, _is_repeating: bool) -> void:
|
||||||
await get_tree().process_frame # Dummy wait for LSP warning otherwise
|
await get_tree().process_frame # Dummy wait for LSP warning otherwise
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,17 +40,12 @@ var mementos_collected: int = 0:
|
||||||
|
|
||||||
var selection: Draggable = null:
|
var selection: Draggable = null:
|
||||||
set(value):
|
set(value):
|
||||||
# this makes sure no accidental context switches can happen while a card is being dragged
|
if selection == value: return
|
||||||
if current_context == DRAG: return
|
|
||||||
|
|
||||||
# Deselect current
|
# Select & highlight new
|
||||||
if selection:
|
if selection: selection.highlighted = false
|
||||||
selection.highlighted = false
|
|
||||||
|
|
||||||
# Select new
|
|
||||||
selection = value
|
selection = value
|
||||||
if selection:
|
if selection: selection.highlighted = true
|
||||||
selection.highlighted = true
|
|
||||||
|
|
||||||
# Are we selecting cards or stickies?
|
# Are we selecting cards or stickies?
|
||||||
if selection is Card:
|
if selection is Card:
|
||||||
|
|
@ -88,6 +83,7 @@ func _smooth(current: Vector2, goal: Vector2, delta: float) -> Vector2:
|
||||||
var k := pow(0.1, 60.0 * delta)
|
var k := pow(0.1, 60.0 * delta)
|
||||||
return (1.0-k) * current + k * goal
|
return (1.0-k) * current + k * goal
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float):
|
func _process(delta: float):
|
||||||
var zone_position := Vector2(notezone.get_screen_position().x + sticky_width / 3.0, sticky_height)
|
var zone_position := Vector2(notezone.get_screen_position().x + sticky_width / 3.0, sticky_height)
|
||||||
|
|
||||||
|
|
@ -207,24 +203,21 @@ func is_in_dropzone(to_check: Draggable) -> bool:
|
||||||
|
|
||||||
|
|
||||||
# Called by notes when a mouse event needs handling
|
# Called by notes when a mouse event needs handling
|
||||||
func handle_mouse_button(input: InputEventMouseButton, to_handle = selection) -> void:
|
func handle_mouse_button(input: InputEventMouseButton, target: Draggable) -> void:
|
||||||
# Prevent dragging multiple nodes at once
|
|
||||||
if current_context == DRAG and selection:
|
|
||||||
return
|
|
||||||
|
|
||||||
# === DRAG START ===
|
# === DRAG START ===
|
||||||
if input.button_index == MOUSE_BUTTON_LEFT and input.pressed:
|
if input.button_index == MOUSE_BUTTON_LEFT and input.is_pressed():
|
||||||
_start_drag(to_handle)
|
_start_drag(target)
|
||||||
return
|
return
|
||||||
|
|
||||||
# === DRAG END ===
|
# === DRAG END ===
|
||||||
if input.button_index == MOUSE_BUTTON_LEFT and not input.pressed:
|
if input.button_index == MOUSE_BUTTON_LEFT and not input.is_released():
|
||||||
_end_drag(to_handle)
|
_end_drag(target)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
## Starts a drag operation for the given draggable
|
## Starts a drag operation for the given draggable
|
||||||
func _start_drag(draggable: Dragg able) -> void:
|
func _start_drag(draggable: Draggable) -> void:
|
||||||
selection = draggable
|
selection = draggable
|
||||||
current_context = DRAG
|
current_context = DRAG
|
||||||
|
|
||||||
|
|
@ -234,24 +227,36 @@ func _start_drag(draggable: Dragg able) -> void:
|
||||||
|
|
||||||
## Ends a drag operation and handles the drop
|
## Ends a drag operation and handles the drop
|
||||||
func _end_drag(draggable: Draggable) -> void:
|
func _end_drag(draggable: Draggable) -> void:
|
||||||
if not draggable: return
|
selection = draggable
|
||||||
|
|
||||||
draggable.end_drag()
|
|
||||||
|
|
||||||
# Let draggable find its own drop target
|
|
||||||
var drop_target := draggable.find_drop_target()
|
|
||||||
|
|
||||||
# Execute the drop
|
|
||||||
if drop_target and Draggable.is_drop_target(drop_target):
|
|
||||||
var result = drop_target.handle_drop(draggable)
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Cleanup and state update
|
# Cleanup and state update
|
||||||
current_context = NAVIGATE
|
current_context = NAVIGATE
|
||||||
|
|
||||||
|
var destination := draggable.end_drag()
|
||||||
|
|
||||||
|
if not destination and draggable is StickyNote:
|
||||||
|
# reclaim if necessary
|
||||||
|
pass
|
||||||
|
|
||||||
|
if destination and destination is Card:
|
||||||
|
# attach / unattach
|
||||||
|
pass
|
||||||
|
|
||||||
|
if destination and destination is StickyNote:
|
||||||
|
# unattach and attach to parent
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Let draggable find its own drop target
|
||||||
|
#var drop_target := draggable.find_drop_target()
|
||||||
|
|
||||||
|
# Execute the drop
|
||||||
|
#if drop_target and Draggable.is_drop_target(drop_target):
|
||||||
|
# var result = drop_target.handle_drop(draggable)
|
||||||
|
# pass
|
||||||
|
|
||||||
# Check win condition if sticky was attached to card
|
# Check win condition if sticky was attached to card
|
||||||
if draggable is StickyNote and draggable.is_attached:
|
if draggable is StickyNote and draggable.is_attached:
|
||||||
check_board_comnpletion()
|
check_board_completion()
|
||||||
|
|
||||||
|
|
||||||
func reclaim_sticky(note: StickyNote):
|
func reclaim_sticky(note: StickyNote):
|
||||||
|
|
@ -259,7 +264,7 @@ func reclaim_sticky(note: StickyNote):
|
||||||
notes.append(note)
|
notes.append(note)
|
||||||
|
|
||||||
|
|
||||||
func check_board_comnpletion():
|
func check_board_completion():
|
||||||
if is_board_complete():
|
if is_board_complete():
|
||||||
if not board_was_completed:
|
if not board_was_completed:
|
||||||
board_was_completed = true
|
board_was_completed = true
|
||||||
|
|
@ -316,20 +321,44 @@ func give_lore_feedback():
|
||||||
complete = true
|
complete = true
|
||||||
|
|
||||||
# Mark area that was hovered over as currently selected
|
# Mark area that was hovered over as currently selected
|
||||||
func handle_hover(to_handle: Area2D) -> void:
|
func handle_hover(draggable: Draggable) -> void:
|
||||||
|
|
||||||
# If we're hovering with the mouse without clicking, that updates our selection
|
# If we're hovering with the mouse without clicking, that updates our selection
|
||||||
if not Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): selection = to_handle
|
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): return
|
||||||
|
|
||||||
|
|
||||||
|
func _sort_by_proximity_and_depth(draggables: Array) -> Array[Draggable]:
|
||||||
|
var result : Array[Draggable] = []
|
||||||
|
result.append_array(draggables)
|
||||||
|
result.sort_custom(_by_mouse)
|
||||||
|
var depth := len(result) * 2
|
||||||
|
for item in result:
|
||||||
|
depth -= 1
|
||||||
|
item.z_index = depth
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
func _nearest_hovered(candidates: Array[Draggable]) -> Draggable:
|
||||||
|
for candidate in candidates:
|
||||||
|
if candidate.mouse_over: return candidate
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func _spatial(a: Draggable, b: Draggable) -> bool:
|
|
||||||
|
func _by_spatial(a: Draggable, b: Draggable) -> bool:
|
||||||
return a.position.x + a.position.y * 100 > b.position.x + b.position.y * 100
|
return a.position.x + a.position.y * 100 > b.position.x + b.position.y * 100
|
||||||
|
|
||||||
|
|
||||||
|
func _by_mouse(a: Draggable, b: Draggable) -> bool:
|
||||||
|
var mouse_pos : Vector2 = get_viewport().get_mouse_position()
|
||||||
|
return (a.position-mouse_pos).length() < (b.position-mouse_pos).length()
|
||||||
|
|
||||||
|
|
||||||
## Call this after bulk loading to fix child order / z-index issues
|
## Call this after bulk loading to fix child order / z-index issues
|
||||||
func _sort_by_positions() -> void:
|
func _sort_by_positions() -> void:
|
||||||
cards.sort_custom(_spatial)
|
cards.sort_custom(_by_spatial)
|
||||||
notes.sort_custom(_spatial)
|
notes.sort_custom(_by_spatial)
|
||||||
|
|
||||||
|
|
||||||
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
||||||
|
|
@ -348,18 +377,16 @@ func handle_drop(draggable: Draggable) -> int:
|
||||||
|
|
||||||
# Takes the inputs for control inputs
|
# Takes the inputs for control inputs
|
||||||
func _input(event) -> void:
|
func _input(event) -> void:
|
||||||
if event.is_action_pressed("ui_cancel"):
|
if event is InputEventAction and event.is_action_pressed("ui_cancel"):
|
||||||
closed.emit()
|
closed.emit()
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
if event is InputEventMouse:
|
|
||||||
# makes sure to pass release events so notes do not get attached to the mouse while the cursor leaves the area.
|
if event is InputEventMouseMotion and not event.is_action_pressed("mouse_left"):
|
||||||
if event is InputEventMouseButton and current_context == DRAG:
|
var candidate := _nearest_hovered(_sort_by_proximity_and_depth(notes))
|
||||||
if event.button_index == MOUSE_BUTTON_LEFT and not event.pressed:
|
if not candidate:
|
||||||
handle_mouse_button(event)
|
candidate = _nearest_hovered(_sort_by_proximity_and_depth(cards))
|
||||||
get_viewport().set_input_as_handled()
|
selection = candidate
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
## Saves board state directly to SaveGame resource
|
## Saves board state directly to SaveGame resource
|
||||||
|
|
|
||||||
|
|
@ -38,55 +38,41 @@ var transfor_arr: Array[Transform2D] = [
|
||||||
@onready var background_sprite: AnimatedSprite2D = $AnimatedSprite2D
|
@onready var background_sprite: AnimatedSprite2D = $AnimatedSprite2D
|
||||||
|
|
||||||
@export var picked_random: bool = false
|
@export var picked_random: bool = false
|
||||||
|
|
||||||
@export var wiggle_strength: float = 0.2
|
@export var wiggle_strength: float = 0.2
|
||||||
@export var wiggle_speed: float = 5
|
@export var wiggle_speed: float = 5
|
||||||
@export_range(1, 2) var scale_bump: float = 1.05
|
@export_range(1, 2) var scale_bump: float = 1.05
|
||||||
@export_range(1.0, 10.0) var bounce_speed: float = 5
|
@export_range(1.0, 10.0) var bounce_speed: float = 5
|
||||||
@export_range(1.0, 2.0) var highlight_brightness: float = 1.4
|
|
||||||
|
@export_color_no_alpha var highlight_color: Color = Color(1.4, 1.4, 1.4)
|
||||||
|
|
||||||
## Override set_highlight to add visual feedback for cards
|
## Override set_highlight to add visual feedback for cards
|
||||||
func set_highlight(value: bool) -> void:
|
func set_highlight(value: bool) -> void:
|
||||||
if value != _highlighted:
|
if value == _highlighted: return
|
||||||
_highlighted = value
|
_highlighted = value
|
||||||
|
|
||||||
|
if scale_tween: scale_tween.kill()
|
||||||
|
if wiggle_tween: wiggle_tween.kill()
|
||||||
|
if brightness_tween: brightness_tween.kill()
|
||||||
|
|
||||||
|
if _highlighted:
|
||||||
|
scale_tween = get_tree().create_tween()
|
||||||
|
scale_tween.tween_property(self, "scale", Vector2(scale_bump, scale_bump), 0.1)
|
||||||
|
wiggle_tween = get_tree().create_tween()
|
||||||
|
wiggle_tween.tween_property(self, "wiggle_intensity", 1, 0.2)
|
||||||
|
brightness_tween = get_tree().create_tween()
|
||||||
|
brightness_tween.set_parallel(true)
|
||||||
|
brightness_tween.tween_property(background_sprite, "modulate", highlight_color, 0.15)
|
||||||
|
brightness_tween.tween_property(label, "modulate", highlight_color, 0.15)
|
||||||
|
else:
|
||||||
|
scale_tween = get_tree().create_tween()
|
||||||
|
scale_tween.tween_property(self, "scale", Vector2(1, 1), 0.3)
|
||||||
|
wiggle_tween = get_tree().create_tween()
|
||||||
|
wiggle_tween.tween_property(self, "wiggle_intensity", 0, 0.5)
|
||||||
|
brightness_tween = get_tree().create_tween()
|
||||||
|
brightness_tween.set_parallel(true)
|
||||||
|
brightness_tween.tween_property(background_sprite, "modulate", Color.WHITE, 0.2)
|
||||||
|
brightness_tween.tween_property(label, "modulate", Color.WHITE, 0.2)
|
||||||
|
|
||||||
if is_inside_tree() and is_node_ready():
|
|
||||||
if scale_tween: scale_tween.kill()
|
|
||||||
if wiggle_tween: wiggle_tween.kill()
|
|
||||||
if brightness_tween: brightness_tween.kill()
|
|
||||||
if _highlighted:
|
|
||||||
scale_tween = get_tree().create_tween()
|
|
||||||
scale_tween.tween_property(self, "scale", Vector2(scale_bump, scale_bump), 0.1)
|
|
||||||
wiggle_tween = get_tree().create_tween()
|
|
||||||
wiggle_tween.tween_property(self, "wiggle_intensity", 1, 0.2)
|
|
||||||
brightness_tween = get_tree().create_tween()
|
|
||||||
brightness_tween.set_parallel(true)
|
|
||||||
brightness_tween.tween_property(background_sprite, "modulate", Color(highlight_brightness, highlight_brightness, highlight_brightness), 0.15)
|
|
||||||
brightness_tween.tween_property(label, "modulate", Color(highlight_brightness, highlight_brightness, highlight_brightness), 0.15)
|
|
||||||
else:
|
|
||||||
scale_tween = get_tree().create_tween()
|
|
||||||
scale_tween.tween_property(self, "scale", Vector2(1, 1), 0.3)
|
|
||||||
wiggle_tween = get_tree().create_tween()
|
|
||||||
wiggle_tween.tween_property(self, "wiggle_intensity", 0, 0.5)
|
|
||||||
brightness_tween = get_tree().create_tween()
|
|
||||||
brightness_tween.set_parallel(true)
|
|
||||||
brightness_tween.tween_property(background_sprite, "modulate", Color.WHITE, 0.2)
|
|
||||||
brightness_tween.tween_property(label, "modulate", Color.WHITE, 0.2)
|
|
||||||
else:
|
|
||||||
if _highlighted:
|
|
||||||
scale = Vector2(scale_bump, scale_bump)
|
|
||||||
wiggle_intensity = 1
|
|
||||||
if background_sprite:
|
|
||||||
background_sprite.modulate = Color(highlight_brightness, highlight_brightness, highlight_brightness)
|
|
||||||
if label:
|
|
||||||
label.modulate = Color(highlight_brightness, highlight_brightness, highlight_brightness)
|
|
||||||
else:
|
|
||||||
scale = Vector2(1,1)
|
|
||||||
wiggle_intensity = 0
|
|
||||||
if background_sprite:
|
|
||||||
background_sprite.modulate = Color.WHITE
|
|
||||||
if label:
|
|
||||||
label.modulate = Color.WHITE
|
|
||||||
|
|
||||||
@export var voice_line: AudioStream = null
|
@export var voice_line: AudioStream = null
|
||||||
@export var is_dragable: bool = false
|
@export var is_dragable: bool = false
|
||||||
|
|
@ -158,6 +144,7 @@ func init(card_name: String = "card", own_id:StringName = "-1") -> void:
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
super._ready()
|
||||||
input_event.connect(_on_input_event)
|
input_event.connect(_on_input_event)
|
||||||
|
|
||||||
_handle_wiggle(0)
|
_handle_wiggle(0)
|
||||||
|
|
@ -202,7 +189,6 @@ func _process(delta: float) -> void:
|
||||||
|
|
||||||
func _handle_wiggle(delta):
|
func _handle_wiggle(delta):
|
||||||
wiggle_pos += delta * wiggle_speed * wiggle_intensity
|
wiggle_pos += delta * wiggle_speed * wiggle_intensity
|
||||||
|
|
||||||
rotation = noise.get_noise_1d(wiggle_pos)*wiggle_strength
|
rotation = noise.get_noise_1d(wiggle_pos)*wiggle_strength
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -212,26 +198,18 @@ func _input(event: InputEvent) -> void:
|
||||||
is_dragged = false
|
is_dragged = false
|
||||||
|
|
||||||
func _on_mouse_entered() -> void:
|
func _on_mouse_entered() -> void:
|
||||||
if not Input.is_action_pressed("mouse_left"):
|
super._on_mouse_entered()
|
||||||
# Do nothing if mouse hovers over sticky_note (it has higher priority)
|
|
||||||
var sticky = get_attached_sticky_note()
|
|
||||||
if sticky and sticky.highlighted:
|
|
||||||
return
|
|
||||||
var board = _get_board()
|
|
||||||
if board:
|
|
||||||
board.handle_hover(self)
|
|
||||||
|
|
||||||
func _on_mouse_exited():
|
func _on_mouse_exited():
|
||||||
highlighted = false
|
super._on_mouse_exited()
|
||||||
if burn_state == burned.SINGED:
|
if burn_state == burned.SINGED:
|
||||||
burn_state = burned.NOT
|
burn_state = burned.NOT
|
||||||
|
|
||||||
func _on_input_event(_viewport, event, _shape_idx):
|
func _on_input_event(_viewport, event, _shape_idx):
|
||||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||||||
var board = _get_board()
|
if highlighted:
|
||||||
if board and highlighted:
|
|
||||||
mouse_offset = get_viewport().get_mouse_position() - position
|
mouse_offset = get_viewport().get_mouse_position() - position
|
||||||
board.handle_mouse_button(event, self)
|
if _get_board(): _get_board().handle_mouse_button(event, self)
|
||||||
|
|
||||||
func _move_card():
|
func _move_card():
|
||||||
if is_dragged:
|
if is_dragged:
|
||||||
|
|
@ -284,19 +262,6 @@ func exchange_sticky_note_with(new_note: StickyNote) -> StickyNote:
|
||||||
attach_sticky_note(new_note)
|
attach_sticky_note(new_note)
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
# This makes sure this node highlights itself when focus has left the sticky note.
|
|
||||||
func check_hover():
|
|
||||||
# Re-trigger hover handling - parent will decide if this should be highlighted
|
|
||||||
_on_mouse_entered()
|
|
||||||
|
|
||||||
func reclaim_sticky_note():
|
|
||||||
var sticky = get_attached_sticky_note()
|
|
||||||
if not sticky:
|
|
||||||
return
|
|
||||||
sticky.tween_transform_to(Transform2D(0, to_global(sticky_note_position)))
|
|
||||||
await sticky.transform_tween_finished
|
|
||||||
sticky.reparent(self)
|
|
||||||
|
|
||||||
|
|
||||||
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
||||||
|
|
||||||
|
|
@ -348,7 +313,7 @@ func find_drop_target() -> Node:
|
||||||
|
|
||||||
## Walks up the scene tree to find the CardBoard
|
## Walks up the scene tree to find the CardBoard
|
||||||
func _get_board() -> CardBoard:
|
func _get_board() -> CardBoard:
|
||||||
var node = get_parent()
|
var node := get_parent()
|
||||||
while node:
|
while node:
|
||||||
if node is CardBoard:
|
if node is CardBoard:
|
||||||
return node
|
return node
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,3 @@ text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
autowrap_mode = 3
|
autowrap_mode = 3
|
||||||
|
|
||||||
[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"]
|
|
||||||
[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"]
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ enum DropResult {
|
||||||
EXCHANGED # Swap occurred, exchanged item needs handling
|
EXCHANGED # Swap occurred, exchanged item needs handling
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mouse_over: bool = false
|
||||||
|
|
||||||
var is_dragged: bool = false:
|
var is_dragged: bool = false:
|
||||||
set(dragged):
|
set(dragged):
|
||||||
is_dragged = dragged
|
is_dragged = dragged
|
||||||
|
|
@ -37,9 +39,35 @@ var _drag_start_position: Vector2
|
||||||
var _mouse_drag_offset: Vector2
|
var _mouse_drag_offset: Vector2
|
||||||
var _drag_source: Node = null # Where the drag started from
|
var _drag_source: Node = null # Where the drag started from
|
||||||
|
|
||||||
|
## === SETUP ###
|
||||||
|
func _ready() -> void:
|
||||||
|
mouse_entered.connect(_on_mouse_entered)
|
||||||
|
mouse_exited.connect(_on_mouse_exited)
|
||||||
|
|
||||||
|
|
||||||
|
func _get_hover_handler() -> Node:
|
||||||
|
var parent := get_parent()
|
||||||
|
while parent and not parent.has_method("handle_hover"):
|
||||||
|
parent = parent.get_parent()
|
||||||
|
return parent
|
||||||
|
|
||||||
## === DRAG LIFECYCLE METHODS ===
|
## === DRAG LIFECYCLE METHODS ===
|
||||||
## Override these in Card and StickyNote for specific behavior
|
## Override these in Card and StickyNote for specific behavior
|
||||||
|
|
||||||
|
func _on_mouse_entered() -> void:
|
||||||
|
prints("Draggable[base]._on_mouse_entered", self, self.name)
|
||||||
|
mouse_over = true
|
||||||
|
var handler := _get_hover_handler()
|
||||||
|
if handler: handler.handle_hover(self)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_mouse_exited() -> void:
|
||||||
|
prints("Draggable[base]._on_mouse_exited", self, self.name)
|
||||||
|
mouse_over = false
|
||||||
|
var handler := _get_hover_handler()
|
||||||
|
if handler: handler.handle_hover(self)
|
||||||
|
|
||||||
|
|
||||||
## Starts a drag operation
|
## Starts a drag operation
|
||||||
func start_drag(mouse_offset: Vector2) -> void:
|
func start_drag(mouse_offset: Vector2) -> void:
|
||||||
_drag_start_position = global_position
|
_drag_start_position = global_position
|
||||||
|
|
@ -60,9 +88,10 @@ func find_drop_target() -> Node:
|
||||||
return get_parent()
|
return get_parent()
|
||||||
|
|
||||||
## Called after drop to clean up drag state
|
## Called after drop to clean up drag state
|
||||||
func end_drag() -> void:
|
func end_drag() -> Node:
|
||||||
is_dragged = false
|
is_dragged = false
|
||||||
_drag_source = null
|
_drag_source = null
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
## Confines this draggable element to stay within screen or container bounds
|
## Confines this draggable element to stay within screen or container bounds
|
||||||
|
|
@ -100,11 +129,3 @@ func _get_container_bounds() -> Rect2:
|
||||||
viewport_size.x - screen_margin * 2,
|
viewport_size.x - screen_margin * 2,
|
||||||
viewport_size.y - screen_margin * 2
|
viewport_size.y - screen_margin * 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# === HELPERS ===
|
|
||||||
## Static helper to check if a node implements DropTarget pattern
|
|
||||||
## DropTarget pattern requires: can_accept_drop(draggable) and handle_drop(draggable)
|
|
||||||
static func is_drop_target(node: Node) -> bool:
|
|
||||||
return node != null and node.has_method("can_accept_drop") and node.has_method("handle_drop")
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,6 @@ _data = {
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="board" type="PanelContainer"]
|
[node name="board" type="PanelContainer"]
|
||||||
z_index = -100
|
|
||||||
material = SubResource("ShaderMaterial_ttqei")
|
material = SubResource("ShaderMaterial_ttqei")
|
||||||
clip_contents = true
|
clip_contents = true
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
|
|
@ -175,6 +174,7 @@ mouse_filter = 1
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(400, 0)
|
custom_minimum_size = Vector2(400, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
mouse_filter = 1
|
||||||
|
|
||||||
[node name="instructions_panel" type="PanelContainer" parent="."]
|
[node name="instructions_panel" type="PanelContainer" parent="."]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
|
||||||
|
|
@ -45,24 +45,19 @@ func set_highlight(value: bool) -> void:
|
||||||
if value != _highlighted:
|
if value != _highlighted:
|
||||||
_highlighted = value
|
_highlighted = value
|
||||||
|
|
||||||
if is_inside_tree() and is_node_ready():
|
if modulate_tween: modulate_tween.kill()
|
||||||
if modulate_tween: modulate_tween.kill()
|
if shift_tween: shift_tween.kill()
|
||||||
if shift_tween: shift_tween.kill()
|
|
||||||
if _highlighted:
|
if _highlighted:
|
||||||
modulate_tween = get_tree().create_tween()
|
modulate_tween = create_tween()
|
||||||
modulate_tween.tween_property(self, "modulate", highlight_color, 0.1)
|
modulate_tween.tween_property(self, "modulate", highlight_color, 0.1)
|
||||||
shift_tween = get_tree().create_tween()
|
shift_tween = create_tween()
|
||||||
shift_tween.tween_property(content, "position", shift_by, 0.2)
|
shift_tween.tween_property(content, "position", shift_by, 0.2)
|
||||||
else:
|
|
||||||
modulate_tween = get_tree().create_tween()
|
|
||||||
modulate_tween.tween_property(self, "modulate", Color(1, 1, 1), 0.3)
|
|
||||||
shift_tween = get_tree().create_tween()
|
|
||||||
shift_tween.tween_property(content, "position", Vector2.ZERO, 0.5)
|
|
||||||
else:
|
else:
|
||||||
if _highlighted:
|
modulate_tween = create_tween()
|
||||||
modulate = Color(1, 1, 1)
|
modulate_tween.tween_property(self, "modulate", Color(1, 1, 1), 0.3)
|
||||||
else:
|
shift_tween = create_tween()
|
||||||
modulate = Color(1, 1, 1)
|
shift_tween.tween_property(content, "position", Vector2.ZERO, 0.5)
|
||||||
|
|
||||||
@export var voice_line: AudioStream = null
|
@export var voice_line: AudioStream = null
|
||||||
@export var is_dragable: bool = false
|
@export var is_dragable: bool = false
|
||||||
|
|
@ -86,19 +81,13 @@ func init(sticky_name: String = "sticky_note", card_id: StringName = "-1") -> vo
|
||||||
sticky_id = card_id
|
sticky_id = card_id
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
super._ready()
|
||||||
label = $Content/Label
|
label = $Content/Label
|
||||||
background_sprite = $Content/BackgroundSprite
|
background_sprite = $Content/BackgroundSprite
|
||||||
content = $Content
|
content = $Content
|
||||||
|
|
||||||
_on_text_updated.call_deferred()
|
_on_text_updated.call_deferred()
|
||||||
|
|
||||||
input_event.connect(_on_input_event)
|
|
||||||
mouse_entered.connect(_on_mouse_entered)
|
|
||||||
mouse_exited.connect(_on_mouse_exited)
|
|
||||||
area_entered.connect(_on_area_enter)
|
|
||||||
area_exited.connect(_on_area_exit)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func _on_text_updated():
|
func _on_text_updated():
|
||||||
label.text = text
|
label.text = text
|
||||||
|
|
@ -108,29 +97,6 @@ func _on_text_updated():
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
_move_sticky_note(delta)
|
_move_sticky_note(delta)
|
||||||
|
|
||||||
func _on_mouse_entered():
|
|
||||||
if not Input.is_action_pressed("mouse_left") and current_handle and current_handle.has_method("handle_hover"):
|
|
||||||
current_handle.handle_hover(self)
|
|
||||||
|
|
||||||
func _on_mouse_exited():
|
|
||||||
highlighted = false
|
|
||||||
|
|
||||||
# Let parent card re-check hover state if this sticky is attached to it
|
|
||||||
var card := attached_to
|
|
||||||
if card:
|
|
||||||
card.check_hover()
|
|
||||||
|
|
||||||
func _on_area_enter(_area: Area2D):
|
|
||||||
pass
|
|
||||||
|
|
||||||
func _on_area_exit(_area: Area2D):
|
|
||||||
pass
|
|
||||||
|
|
||||||
func _on_input_event(_viewport, event, _shape_idx):
|
|
||||||
if event is InputEventMouseButton and current_handle and current_handle.has_method("handle_mouse_button"):
|
|
||||||
if (event.button_index == MOUSE_BUTTON_LEFT and event.pressed) or event.button_index == MOUSE_BUTTON_RIGHT:
|
|
||||||
mouse_offset = get_viewport().get_mouse_position() - global_position
|
|
||||||
current_handle.handle_mouse_button(event, self)
|
|
||||||
|
|
||||||
## frame rate independent FIR smoothing filter
|
## frame rate independent FIR smoothing filter
|
||||||
func _smooth(current: Vector2, goal: Vector2, delta: float) -> Vector2:
|
func _smooth(current: Vector2, goal: Vector2, delta: float) -> Vector2:
|
||||||
|
|
@ -171,31 +137,25 @@ func tween_transform_to(target: Transform2D, duration: float = 0.25) ->void:
|
||||||
|
|
||||||
# === DRAG LIFECYCLE OVERRIDES ===
|
# === DRAG LIFECYCLE OVERRIDES ===
|
||||||
|
|
||||||
## Start drag: if in panel, immediately move to board
|
func end_drag() -> Node:
|
||||||
func start_drag(offset: Vector2) -> void:
|
super.end_drag()
|
||||||
super.start_drag(offset)
|
return _find_drop_target()
|
||||||
|
|
||||||
# If attached to a card, detach it first
|
|
||||||
var card := attached_to
|
|
||||||
if card:
|
|
||||||
card.remove_sticky_note()
|
|
||||||
|
|
||||||
|
|
||||||
## Find best drop target: Card > Panel > Board (in priority order)
|
## Find best drop target: Card > Panel > Board (in priority order)
|
||||||
func find_drop_target() -> Node:
|
func _find_drop_target() -> Node:
|
||||||
# Priority 1: Check for overlapping cards in dropzone
|
# Priority 1: Check for overlapping cards in dropzone
|
||||||
|
var closest : Card = null
|
||||||
for area in get_overlapping_areas():
|
for area in get_overlapping_areas():
|
||||||
if area is Card and Draggable.is_drop_target(area):
|
if area is StickyNote and not area.is_attached: continue # Can only drop on attached stickies
|
||||||
|
|
||||||
|
if area is Card:
|
||||||
|
if (not closest) or ((closest.position-position).length() < (area.position - position).length()):
|
||||||
|
closest = area
|
||||||
return area
|
return area
|
||||||
|
|
||||||
# Priority 2: Check if dropped outside dropzone (over panel area)
|
|
||||||
if current_handle and not current_handle.is_in_dropzone(self):
|
|
||||||
var target_panel := _find_nearest_panel()
|
|
||||||
if target_panel:
|
|
||||||
return target_panel
|
|
||||||
|
|
||||||
# Priority 3: Default to board (stay loose in dropzone)
|
# Priority 3: Default to board (stay loose in dropzone)
|
||||||
return current_handle
|
return null
|
||||||
|
|
||||||
## Find the nearest panel that can accept this sticky
|
## Find the nearest panel that can accept this sticky
|
||||||
func _find_nearest_panel() -> StickyNotePanel:
|
func _find_nearest_panel() -> StickyNotePanel:
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ radius = 48.0
|
||||||
height = 312.0
|
height = 312.0
|
||||||
|
|
||||||
[node name="sticky-note" type="Area2D"]
|
[node name="sticky-note" type="Area2D"]
|
||||||
z_index = 1
|
|
||||||
collision_layer = 2
|
collision_layer = 2
|
||||||
collision_mask = 6
|
collision_mask = 6
|
||||||
priority = 100
|
priority = 100
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,6 @@ class_name Playable
|
||||||
## Awaitable that encapsulates the core interaction with this Playable
|
## Awaitable that encapsulates the core interaction with this Playable
|
||||||
func play() -> void:
|
func play() -> void:
|
||||||
await get_tree().process_frame # Dummy wait so this is a coroutine
|
await get_tree().process_frame # Dummy wait so this is a coroutine
|
||||||
|
|
||||||
|
func handle_hover(area: Draggable):
|
||||||
|
prints("Playable[base].handle_hover", area, area.name)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue