frame-of-mind/src/logic-scenes/board/sticky-note.gd

185 lines
5.2 KiB
GDScript3
Raw Normal View History

extends Draggable
2023-10-12 16:25:21 +00:00
class_name StickyNote
var sticky_id
var parent_id
2025-01-31 02:22:07 +00:00
var sibling: StickyNote
var shift_tween: Tween
var modulate_tween: Tween
# cannot be explicitly typed, as this can be both handled by picker and physics-board
var current_handle: Node
2023-04-19 16:53:24 +00:00
var position_locked: bool = false
## Computed property: Is this currently attached to a card
2026-01-18 09:48:03 +00:00
var is_attached : bool:
get: return get_parent() is Card
## Replaces the need for tracking attached_to as state
var attached_to: Card:
get: return get_parent() as Card if is_attached else null
2023-09-23 14:19:58 +00:00
signal transform_tween_finished
@onready var background_sprite: AnimatedSprite2D = %BackgroundSprite
2023-04-19 16:53:24 +00:00
@export var text: String = "" :
2024-09-15 09:30:31 +00:00
set (value):
if is_node_ready():
_on_text_updated.call_deferred()
2024-09-15 09:30:31 +00:00
text = value
var content: Node2D
var label: Label
@export var picked_random: bool = false
@export var shift_by: Vector2 = Vector2(-32, 0)
@export_color_no_alpha var highlight_color: Color = Color(1.5, 1.5, 1.5)
2024-09-15 09:30:31 +00:00
## Override set_highlight to add visual feedback for sticky notes
func set_highlight(value: bool) -> void:
if value != _highlighted:
_highlighted = value
2026-01-18 09:48:03 +00:00
if modulate_tween: modulate_tween.kill()
if shift_tween: shift_tween.kill()
if _highlighted:
modulate_tween = create_tween()
modulate_tween.tween_property(self, "modulate", highlight_color, 0.1)
shift_tween = create_tween()
shift_tween.tween_property(content, "position", shift_by, 0.2)
else:
2026-01-18 09:48:03 +00:00
modulate_tween = create_tween()
modulate_tween.tween_property(self, "modulate", Color(1, 1, 1), 0.3)
shift_tween = create_tween()
shift_tween.tween_property(content, "position", Vector2.ZERO, 0.5)
2023-04-19 16:53:24 +00:00
@export var voice_line: AudioStream = null
@export var is_dragable: bool = false
2023-07-14 19:36:20 +00:00
var mouse_offset: Vector2
2023-04-19 16:53:24 +00:00
@onready var diameter := 312.0
@export_range(1.0, 10.0) var bounce_speed: float = 8
## Computed property: Check if on the board (dropzone)
## Replaces on_board state tracking
var on_board: bool:
get:
var parent := get_parent()
return parent != null and parent.name == "dropzone"
2025-02-24 15:14:08 +00:00
func init(sticky_name: String = "sticky_note", card_id: StringName = "-1") -> void:
name = sticky_name
text = sticky_name
2025-05-30 14:10:44 +00:00
parent_id = StringName(card_id.rsplit(".", false, 1)[0])
sticky_id = card_id
2023-04-19 16:53:24 +00:00
func _ready() -> void:
2026-01-18 09:48:03 +00:00
super._ready()
label = $Content/Label
background_sprite = $Content/BackgroundSprite
content = $Content
_on_text_updated.call_deferred()
func _on_text_updated():
label.text = text
background_sprite.frame = text.hash() % background_sprite.sprite_frames.get_frame_count(background_sprite.animation)
2023-04-19 16:53:24 +00:00
func _process(delta: float) -> void:
_move_sticky_note(delta)
## frame rate independent FIR smoothing filter
func _smooth(current: Vector2, goal: Vector2, delta: float) -> Vector2:
var k := pow(0.1, 60.0 * delta)
return (1.0-k) * current + k * goal
2026-01-18 09:48:03 +00:00
func _move_sticky_note(delta: float) -> void:
2024-09-15 09:30:31 +00:00
if is_dragged:
2026-01-16 19:46:16 +00:00
update_drag_position(get_viewport().get_mouse_position())
return
2026-01-18 09:48:03 +00:00
if is_attached:
var card := attached_to
position = _smooth(position, card.sticky_note_position, delta)
2026-01-18 09:48:03 +00:00
2023-07-02 13:10:33 +00:00
var transform_tween: Tween
func tween_transform_to(target: Transform2D, duration: float = 0.25) ->void:
# Validate position to prevent teleporting
if not is_finite(target.origin.x) or not is_finite(target.origin.y):
push_warning("StickyNote.tween_transform_to: Invalid position, skipping tween")
transform_tween_finished.emit()
return
2026-01-18 09:48:03 +00:00
if transform_tween and transform_tween.is_running():
transform_tween.stop()
transform_tween = create_tween()
transform_tween.tween_property(self, "transform", target, duration)
2024-09-15 09:30:31 +00:00
await transform_tween.finished
transform_tween_finished.emit()
2023-11-01 22:19:47 +00:00
2026-01-16 19:46:16 +00:00
# === DRAG LIFECYCLE OVERRIDES ===
2026-01-18 09:48:03 +00:00
func end_drag() -> Node:
super.end_drag()
return _find_drop_target()
2026-01-16 19:46:16 +00:00
## Find best drop target: Card > Panel > Board (in priority order)
2026-01-18 09:48:03 +00:00
func _find_drop_target() -> Node:
2026-01-16 19:46:16 +00:00
# Priority 1: Check for overlapping cards in dropzone
2026-01-18 09:48:03 +00:00
var closest : Card = null
2026-01-16 19:46:16 +00:00
for area in get_overlapping_areas():
2026-01-18 09:48:03 +00:00
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
2026-01-16 19:46:16 +00:00
return area
2026-01-18 09:48:03 +00:00
2026-01-16 19:46:16 +00:00
# Priority 3: Default to board (stay loose in dropzone)
2026-01-18 09:48:03 +00:00
return null
2026-01-16 19:46:16 +00:00
## Find the nearest panel that can accept this sticky
func _find_nearest_panel() -> StickyNotePanel:
if not current_handle or not current_handle.has_node("HBoxContainer/ScrollContainer/VBoxContainer"):
return null
2026-01-18 09:48:03 +00:00
2026-01-16 19:46:16 +00:00
var panel_container := current_handle.get_node("HBoxContainer/ScrollContainer/VBoxContainer")
var sticky_rect := Rect2(global_position - Vector2(diameter/2, 10), Vector2(diameter/2, 10))
2026-01-18 09:48:03 +00:00
2026-01-16 19:46:16 +00:00
# First pass: look for empty panels we're hovering over
for panel in panel_container.get_children():
if panel is StickyNotePanel:
if panel.is_empty() and panel.get_global_rect().intersects(sticky_rect):
return panel
2026-01-18 09:48:03 +00:00
2026-01-16 19:46:16 +00:00
# Second pass: if no empty panel found, find first empty panel
for panel in panel_container.get_children():
if panel is StickyNotePanel and panel.is_empty():
return panel
2026-01-18 09:48:03 +00:00
2026-01-16 19:46:16 +00:00
# No empty panels found - will need to create one (handled by board)
return null
func confine_to_screen() -> void:
if attached_to is not Card: super.confine_to_screen()