extends Draggable class_name StickyNote var sticky_id var parent_id : StringName 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 var position_locked: bool = false ## Computed property: Is this currently attached to a card 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 @onready var background_sprite: AnimatedSprite2D = %BackgroundSprite @export var text: String = "" : set (value): if is_node_ready(): _on_text_updated.call_deferred() text = value var content: Node2D var label: Label @export var shift_by: Vector2 = Vector2(-32, 0) @export_color_no_alpha var highlight_color: Color = Color(1.5, 1.5, 1.5) ## Override set_highlight to add visual feedback for sticky notes func set_highlight(value: bool) -> void: if value != _highlighted: _highlighted = value 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: 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) @export var voice_line: AudioStream = null @export var is_draggable: bool = false var mouse_offset: Vector2 @onready var diameter := 312.0 @export_range(1.0, 10.0) var bounce_speed: float = 8 func init(sticky_name: String = "sticky_note", card_id: StringName = "-1") -> void: name = sticky_name text = sticky_name parent_id = StringName(card_id.rsplit(".", false, 1)[0]) sticky_id = card_id func _ready() -> void: 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) func _process(_delta: float) -> void: if is_dragged: update_drag_position(get_viewport().get_mouse_position()) # === DRAG LIFECYCLE OVERRIDES === ## End drag operation and return the node we were dropped on func end_drag() -> Node: super.end_drag() return _find_drop_target() ## Find best drop target: Card > Panel > Board (in priority order) func _find_drop_target() -> Node: # Priority 1: Check for overlapping cards in dropzone var closest : Card = null for area in get_overlapping_areas(): if area is StickyNote and not area.is_attached: continue # Can only drop on attached stickies if area is Card: if (not closest) or ((area.position - position).length() < (closest.position - position).length()): closest = area return closest ## Sticky notes are exempt from confinement if stuck to a card func confine_to_screen() -> void: if attached_to is not Card: super.confine_to_screen()