Compare commits

...

3 Commits

5 changed files with 98 additions and 86 deletions

View File

@ -262,7 +262,6 @@ func add_card(card: Card, re_parent:bool = true):
add_child(card) add_child(card)
insert_area(dropzone, card) insert_area(dropzone, card)
card.position = _generate_random_position() card.position = _generate_random_position()
card.set_owner(self)
card.is_dragable = true card.is_dragable = true
## Unified function to reclaim any sticky note to a panel ## Unified function to reclaim any sticky note to a panel
@ -270,29 +269,28 @@ func add_card(card: Card, re_parent:bool = true):
func reclaim_sticky_to_panel(sticky: StickyNote, animate: bool = true, prefer_panel_index: int = -1) -> void: func reclaim_sticky_to_panel(sticky: StickyNote, animate: bool = true, prefer_panel_index: int = -1) -> void:
# Find or create target panel # Find or create target panel
var target_panel: StickyNotePanel = null var target_panel: StickyNotePanel = null
# Try preferred panel first (for exchanges) # Try preferred panel first (for exchanges)
if prefer_panel_index >= 0 and prefer_panel_index < sticky_note_container.get_child_count(): if prefer_panel_index >= 0 and prefer_panel_index < sticky_note_container.get_child_count():
var panel = sticky_note_container.get_child(prefer_panel_index) var panel = sticky_note_container.get_child(prefer_panel_index)
if panel is StickyNotePanel and panel.is_empty(): if panel is StickyNotePanel and panel.is_empty():
target_panel = panel target_panel = panel
# Find any empty panel # Find any empty panel
if not target_panel: if not target_panel:
for panel in sticky_note_container.get_children(): for panel in sticky_note_container.get_children():
if panel is StickyNotePanel and panel.is_empty(): if panel is StickyNotePanel and panel.is_empty():
target_panel = panel target_panel = panel
break break
# Create new panel if needed # Create new panel if needed
if not target_panel: if not target_panel:
target_panel = StickyNotePanel.new() target_panel = StickyNotePanel.new()
sticky_note_container.add_child(target_panel, true, Node.INTERNAL_MODE_DISABLED) sticky_note_container.add_child(target_panel, true, Node.INTERNAL_MODE_DISABLED)
target_panel.set_owner(self)
# Attach sticky to panel (handles all state setup and animation) # Attach sticky to panel (handles all state setup and animation)
target_panel.attatch_sticky_note(sticky, self, animate) target_panel.attatch_sticky_note(sticky, self, animate)
# Clean up other empty panels # Clean up other empty panels
if animate: # Only clean up during interactive use, not initial setup if animate: # Only clean up during interactive use, not initial setup
for panel in sticky_note_container.get_children(): for panel in sticky_note_container.get_children():
@ -467,9 +465,6 @@ func insert_area(parent: Control, node: Area2D):
if not node in parent.get_children(): if not node in parent.get_children():
node.reparent(parent, false) # Don't preserve global transform - we set positions explicitly node.reparent(parent, false) # Don't preserve global transform - we set positions explicitly
if node is StickyNote:
node.on_board = true
node.owner = self
if children.size() > 0: if children.size() > 0:
children.erase(node) children.erase(node)
@ -478,7 +473,6 @@ func insert_area(parent: Control, node: Area2D):
parent.move_child(node, i) parent.move_child(node, i)
if node is StickyNote: if node is StickyNote:
node.attached_to = self
node.is_dragable = true node.is_dragable = true
## Sorts all children in dropzone by their Y position ## Sorts all children in dropzone by their Y position
@ -526,8 +520,6 @@ func handle_drop(draggable: Draggable) -> int:
# Handle sticky note drop # Handle sticky note drop
var sticky = draggable as StickyNote var sticky = draggable as StickyNote
insert_area(dropzone, sticky) insert_area(dropzone, sticky)
sticky.attached_to = self
sticky.on_board = true
sticky.is_dragable = true sticky.is_dragable = true
# Reset visual state # Reset visual state
sticky.rotation = 0.0 sticky.rotation = 0.0
@ -789,7 +781,6 @@ func initialise_from_save(savegame: SaveGame) -> void:
# Add to board first # Add to board first
add_child(card) add_child(card)
card.set_owner(self)
card.is_dragable = true card.is_dragable = true
cards_by_name[card.name] = card cards_by_name[card.name] = card
card.picked_random = savegame.board_randoms.has(card.card_id) card.picked_random = savegame.board_randoms.has(card.card_id)
@ -812,7 +803,6 @@ func initialise_from_save(savegame: SaveGame) -> void:
if cards_by_name.has(card_name): if cards_by_name.has(card_name):
# Must add sticky to scene tree BEFORE attach_sticky_note() can reparent it # Must add sticky to scene tree BEFORE attach_sticky_note() can reparent it
add_child(sticky) add_child(sticky)
sticky.set_owner(self)
sticky.current_handle = self # Required for input handling sticky.current_handle = self # Required for input handling
cards_by_name[card_name].attach_sticky_note(sticky) cards_by_name[card_name].attach_sticky_note(sticky)
print_debug(" Sticky '%s' attached to card '%s'" % [sticky.name, card_name]) print_debug(" Sticky '%s' attached to card '%s'" % [sticky.name, card_name])
@ -833,10 +823,7 @@ func initialise_from_save(savegame: SaveGame) -> void:
# Add to board first # Add to board first
add_child(sticky) add_child(sticky)
sticky.set_owner(self)
sticky.current_handle = self # Required for input handling sticky.current_handle = self # Required for input handling
sticky.on_board = true
sticky.attached_to = self
sticky.is_dragable = true sticky.is_dragable = true
# Move to dropzone and set position (position must be set after adding to scene) # Move to dropzone and set position (position must be set after adding to scene)

View File

@ -14,7 +14,6 @@ enum burned {
var compatible_sticky_notes: Array[StickyNote] = [] var compatible_sticky_notes: Array[StickyNote] = []
@export var evil_sticky_notes: Array[StickyNote] = [] @export var evil_sticky_notes: Array[StickyNote] = []
var own_sticky_notes: Array[StickyNote] = [] var own_sticky_notes: Array[StickyNote] = []
var current_sticky_note: StickyNote = null
var wiggle_pos: float = 0 var wiggle_pos: float = 0
var wiggle_intensity: float = 0 var wiggle_intensity: float = 0
var noise: Noise = FastNoiseLite.new() var noise: Noise = FastNoiseLite.new()
@ -215,11 +214,12 @@ func _input(event: InputEvent) -> void:
func _on_mouse_entered() -> void: func _on_mouse_entered() -> void:
if not Input.is_action_pressed("mouse_left"): if not Input.is_action_pressed("mouse_left"):
# Do nothing if mouse hovers over sticky_note (it has higher priority) # Do nothing if mouse hovers over sticky_note (it has higher priority)
if has_sticky_note_attached(): var sticky = get_attached_sticky_note()
if current_sticky_note and current_sticky_note.highlighted: if sticky and sticky.highlighted:
return return
if "handle_hover" in owner: var board = _get_board()
owner.handle_hover(self) if board:
board.handle_hover(self)
func _on_mouse_exited(): func _on_mouse_exited():
highlighted = false highlighted = false
@ -228,9 +228,10 @@ func _on_mouse_exited():
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:
if "handle_mouse_button" in owner and highlighted: var board = _get_board()
if board and highlighted:
mouse_offset = get_viewport().get_mouse_position() - position mouse_offset = get_viewport().get_mouse_position() - position
owner.handle_mouse_button(event, self) board.handle_mouse_button(event, self)
func _move_card(): func _move_card():
if is_dragged: if is_dragged:
@ -249,8 +250,7 @@ func get_attached_sticky_note() -> StickyNote:
func preview_sticky_note(sticky_note: StickyNote): func preview_sticky_note(sticky_note: StickyNote):
if not is_instance_valid(sticky_note): if not is_instance_valid(sticky_note):
return return
sticky_note.reparent(self.get_parent()) # Keep sticky in current parent during preview (just move it visually)
sticky_note.attached_to = self
# Use a safe transform with validated position # Use a safe transform with validated position
var target_pos := global_position + sticky_note_position var target_pos := global_position + sticky_note_position
if is_finite(target_pos.x) and is_finite(target_pos.y): if is_finite(target_pos.x) and is_finite(target_pos.y):
@ -264,11 +264,7 @@ func attach_sticky_note(sticky_note: StickyNote) -> bool:
sticky_note.reparent(self) sticky_note.reparent(self)
sticky_note.position = sticky_note_position sticky_note.position = sticky_note_position
sticky_note.on_board = false
sticky_note.is_dragable = false sticky_note.is_dragable = false
current_sticky_note = sticky_note
#var former_parent = sticky_note.attached_to
sticky_note.attached_to = self
if name == "c_hit" and sticky_note.name == "c_effort" and Steamworks.has_initialized: if name == "c_hit" and sticky_note.name == "c_effort" and Steamworks.has_initialized:
Steam.setAchievement("FIGHT_FOR_GOOD") Steam.setAchievement("FIGHT_FOR_GOOD")
@ -277,12 +273,10 @@ func attach_sticky_note(sticky_note: StickyNote) -> bool:
return true return true
func remove_sticky_note() -> StickyNote: func remove_sticky_note() -> StickyNote:
var former_child:StickyNote = get_attached_sticky_note() var former_child: StickyNote = get_attached_sticky_note()
current_sticky_note = null if not former_child:
return null
former_child.reparent(get_parent()) former_child.reparent(get_parent())
former_child.owner = self.owner
former_child.on_board = true
former_child.attached_to = owner
return former_child return former_child
func exchange_sticky_note_with(new_note: StickyNote) -> StickyNote: func exchange_sticky_note_with(new_note: StickyNote) -> StickyNote:
@ -292,15 +286,16 @@ func exchange_sticky_note_with(new_note: StickyNote) -> StickyNote:
# This makes sure this node highlights itself when focus has left the sticky note. # This makes sure this node highlights itself when focus has left the sticky note.
func check_hover(): func check_hover():
# Re-trigger hover handling - owner will decide if this should be highlighted # Re-trigger hover handling - parent will decide if this should be highlighted
_on_mouse_entered() _on_mouse_entered()
func reclaim_sticky_note(): func reclaim_sticky_note():
current_sticky_note.on_board = false var sticky = get_attached_sticky_note()
current_sticky_note.tween_transform_to(Transform2D(0, to_global(sticky_note_position))) if not sticky:
await current_sticky_note.transform_tween_finished return
current_sticky_note.reparent(self) sticky.tween_transform_to(Transform2D(0, to_global(sticky_note_position)))
current_sticky_note.owner = self.owner await sticky.transform_tween_finished
sticky.reparent(self)
# === DROP TARGET PATTERN IMPLEMENTATION === # === DROP TARGET PATTERN IMPLEMENTATION ===
@ -346,4 +341,16 @@ func get_last_exchanged_sticky() -> StickyNote:
## Cards always drop back to board dropzone ## Cards always drop back to board dropzone
func find_drop_target() -> Node: func find_drop_target() -> Node:
return owner if owner is CardBoard else get_parent() return _get_board()
# === HELPER FUNCTIONS ===
## Walks up the scene tree to find the CardBoard
func _get_board() -> CardBoard:
var node = get_parent()
while node:
if node is CardBoard:
return node
node = node.get_parent()
return null

View File

@ -172,6 +172,7 @@ mouse_filter = 1
[node name="ScrollContainer" type="ScrollContainer" parent="HBoxContainer"] [node name="ScrollContainer" type="ScrollContainer" parent="HBoxContainer"]
clip_contents = false clip_contents = false
custom_minimum_size = Vector2(400, 0)
layout_mode = 2 layout_mode = 2
horizontal_scroll_mode = 0 horizontal_scroll_mode = 0

View File

@ -7,14 +7,17 @@ var parent_id
var sibling: StickyNote var sibling: StickyNote
var shift_tween: Tween var shift_tween: Tween
var modulate_tween: Tween var modulate_tween: Tween
var attached_to: Node = null:
set(new_attatchement):
attached_to = new_attatchement
# cannot be explicitly typed, as this can be both handled by picker and physics-board # cannot be explicitly typed, as this can be both handled by picker and physics-board
var current_handle: Node var current_handle: Node
var position_locked: bool = false var position_locked: bool = false
## Computed property: Returns the current attachment (parent node)
## Replaces the need for tracking attached_to as state
var attached_to: Node:
get: return get_parent()
signal transform_tween_finished signal transform_tween_finished
@onready var background_sprite: AnimatedSprite2D = %BackgroundSprite @onready var background_sprite: AnimatedSprite2D = %BackgroundSprite
@ -63,7 +66,13 @@ var mouse_offset: Vector2
@onready var diameter := 312.0 @onready var diameter := 312.0
@export_range(1.0, 10.0) var bounce_speed: float = 8 @export_range(1.0, 10.0) var bounce_speed: float = 8
var on_board: bool = false
## 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"
func init(sticky_name: String = "sticky_note", card_id: StringName = "-1") -> void: func init(sticky_name: String = "sticky_note", card_id: StringName = "-1") -> void:
name = sticky_name name = sticky_name
@ -102,27 +111,33 @@ func _process(delta: float) -> void:
_move_sticky_note() _move_sticky_note()
func _on_mouse_entered(): func _on_mouse_entered():
if not Input.is_action_pressed("mouse_left") and "handle_hover" in current_handle: if not Input.is_action_pressed("mouse_left") and current_handle and current_handle.has_method("handle_hover"):
current_handle.handle_hover(self) current_handle.handle_hover(self)
func _on_mouse_exited(): func _on_mouse_exited():
highlighted = false highlighted = false
# Let parent card re-check hover state if this sticky is attached to it # Let parent card re-check hover state if this sticky is attached to it
if is_sticky_note_attached() and "check_hover" in attached_to: if is_sticky_note_attached():
attached_to.check_hover() var card = get_parent()
if card and card.has_method("check_hover"):
card.check_hover()
func _on_area_enter(area: Area2D): func _on_area_enter(area: Area2D):
# Handle sticky note panel gap creation # Handle sticky note panel gap creation
if area is StickyNote and is_sticky_note_in_panel() and not is_dragged: if area is StickyNote and is_sticky_note_in_panel() and not is_dragged:
attached_to.create_gap() var panel = get_parent() as StickyNotePanel
if panel:
panel.create_gap()
func _on_area_exit(area: Area2D): func _on_area_exit(area: Area2D):
# Handle sticky note panel gap collapse # Handle sticky note panel gap collapse
if area is StickyNote and is_sticky_note_in_panel(): if area is StickyNote and is_sticky_note_in_panel():
attached_to.collapse_gap() var panel = get_parent() as StickyNotePanel
if panel:
panel.collapse_gap()
func _on_input_event(_viewport, event, _shape_idx): func _on_input_event(_viewport, event, _shape_idx):
if event is InputEventMouseButton and "handle_mouse_button" in current_handle: 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: 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 mouse_offset = get_viewport().get_mouse_position() - global_position
current_handle.handle_mouse_button(event, self) current_handle.handle_mouse_button(event, self)
@ -132,12 +147,12 @@ func _move_sticky_note():
update_drag_position(get_viewport().get_mouse_position()) update_drag_position(get_viewport().get_mouse_position())
func is_sticky_note_attached() -> bool: func is_sticky_note_attached() -> bool:
# FIXME: this breaks if attatched to is previousely freed because GODOT IS FUCKING STUPID var parent = get_parent()
return attached_to is Card return is_instance_valid(parent) and parent is Card
func is_sticky_note_in_panel() -> bool: func is_sticky_note_in_panel() -> bool:
## fixme ~> see above var parent = get_parent()
return attached_to is StickyNotePanel return is_instance_valid(parent) and parent is StickyNotePanel
var transform_tween: Tween var transform_tween: Tween
@ -170,11 +185,11 @@ func start_drag(offset: Vector2) -> void:
# If attached to a card, detach it first # If attached to a card, detach it first
if is_sticky_note_attached(): if is_sticky_note_attached():
var card := attached_to as Card var card := get_parent() as Card
if card and card.has_method("remove_sticky_note"): if card and card.has_method("remove_sticky_note"):
card.remove_sticky_note() card.remove_sticky_note()
# If in panel, immediately reparent to board for dragging # If in panel, immediately reparent to board dropzone for dragging
if _came_from_panel and current_handle: if _came_from_panel and current_handle:
var board := current_handle var board := current_handle
var dropzone := board.get_node_or_null("HBoxContainer/dropzone") var dropzone := board.get_node_or_null("HBoxContainer/dropzone")
@ -182,8 +197,6 @@ func start_drag(offset: Vector2) -> void:
reparent(dropzone) reparent(dropzone)
else: else:
reparent(board) reparent(board)
on_board = true
attached_to = board
## 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:

View File

@ -11,43 +11,40 @@ var ancor_position: Vector2
func _init(cstm_minimum_size: Vector2 = minimum_size, note_position: Vector2 = Vector2(105, 57)) -> void: func _init(cstm_minimum_size: Vector2 = minimum_size, note_position: Vector2 = Vector2(105, 57)) -> void:
minimum_size = cstm_minimum_size minimum_size = cstm_minimum_size
ancor_position = note_position ancor_position = note_position
mouse_filter = MOUSE_FILTER_PASS mouse_filter = MOUSE_FILTER_PASS
self_modulate = Color(1, 1, 1, 0) self_modulate = Color(1, 1, 1, 0)
func _ready(): func _ready():
custom_minimum_size = Vector2(custom_minimum_size.x, 0) custom_minimum_size = Vector2(custom_minimum_size.x, 0)
var is_attatching: bool = false var is_attatching: bool = false
func attatch_sticky_note(attatchment: StickyNote, custom_owner: Node, animate:bool = true): func attatch_sticky_note(attatchment: StickyNote, custom_handle: Node, animate:bool = true):
is_attatching = true is_attatching = true
attached_sticky_note = attatchment attached_sticky_note = attatchment
attatchment.current_handle = custom_owner attatchment.current_handle = custom_handle
attatchment.owner = custom_owner
# Expand panel height # Expand panel height
if animate: if animate:
var height_tween: Tween = create_tween() var height_tween: Tween = create_tween()
height_tween.tween_property(self, "custom_minimum_size", minimum_size, 0.1) height_tween.tween_property(self, "custom_minimum_size", minimum_size, 0.1)
else: else:
custom_minimum_size = minimum_size custom_minimum_size = minimum_size
# Position sticky # Position sticky
if animate: if animate:
await get_tree().process_frame await get_tree().process_frame
attatchment.on_board = false
attatchment.z_index = 125 # On top during animation attatchment.z_index = 125 # On top during animation
# Reparent while keeping world position for smooth animation # Reparent while keeping world position for smooth animation
attatchment.reparent(self, true) attatchment.reparent(self, true)
attatchment.attached_to = self
# Tween to anchor position in panel's coordinate space # Tween to anchor position in panel's coordinate space
var tween := create_tween().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_BACK) var tween := create_tween().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_BACK)
tween.tween_property(attatchment, "position", ancor_position, 0.7) tween.tween_property(attatchment, "position", ancor_position, 0.7)
tween.tween_property(attatchment, "rotation", 0.0, 0.7) tween.tween_property(attatchment, "rotation", 0.0, 0.7)
tween.parallel().tween_property(attatchment, "scale", Vector2.ONE, 0.7) tween.parallel().tween_property(attatchment, "scale", Vector2.ONE, 0.7)
await tween.finished await tween.finished
attatchment.z_index = 0 attatchment.z_index = 0
else: else:
# Immediate placement (for initial board setup) # Immediate placement (for initial board setup)
@ -55,26 +52,25 @@ func attatch_sticky_note(attatchment: StickyNote, custom_owner: Node, animate:bo
attatchment.reparent(self) attatchment.reparent(self)
else: else:
add_child(attatchment) add_child(attatchment)
attatchment.on_board = false
attatchment.attached_to = self
attatchment.position = ancor_position attatchment.position = ancor_position
attatchment.rotation = 0.0 attatchment.rotation = 0.0
attatchment.scale = Vector2.ONE attatchment.scale = Vector2.ONE
is_attatching = false is_attatching = false
var is_gapped: bool = false var is_gapped: bool = false
func create_gap(): func create_gap():
var self_id := get_parent().get_children().find(self) var self_id := get_parent().get_children().find(self)
var next_id = min(self_id + 1, get_parent().get_child_count() - 1) var next_id = min(self_id + 1, get_parent().get_child_count() - 1)
var previous_id = max(self_id - 1, 0) var previous_id = max(self_id - 1, 0)
if not (is_gapped or get_parent().get_child(next_id).attached_sticky_note.is_dragged or get_parent().get_child(previous_id).attached_sticky_note.is_dragged) and owner.current_context == CardBoard.DRAG: var board = _get_board()
if not (is_gapped or get_parent().get_child(next_id).attached_sticky_note.is_dragged or get_parent().get_child(previous_id).attached_sticky_note.is_dragged) and board and board.current_context == CardBoard.DRAG:
is_gapped = true is_gapped = true
var height_tween: Tween = create_tween() var height_tween: Tween = create_tween()
height_tween.tween_property(self, "custom_minimum_size", minimum_size*Vector2(1.0, 1.8), 0.1) height_tween.tween_property(self, "custom_minimum_size", minimum_size*Vector2(1.0, 1.8), 0.1)
get_parent().get_child(next_id).collapse_gap() get_parent().get_child(next_id).collapse_gap()
if not get_parent().get_children().find(self) == 0: get_parent().get_child(previous_id).collapse_gap() if not get_parent().get_children().find(self) == 0: get_parent().get_child(previous_id).collapse_gap()
@ -88,11 +84,13 @@ var invalid: bool = false
func clear_if_empty(): func clear_if_empty():
if !is_empty(): return if !is_empty(): return
invalid = true invalid = true
if attached_sticky_note.attached_to == self: attached_sticky_note.attached_to = null # No need to manually clear attached_to - reparenting handles it
var height_tween: Tween = create_tween() var height_tween: Tween = create_tween()
height_tween.tween_property(self, "custom_minimum_size", Vector2.ZERO, 0.3) height_tween.tween_property(self, "custom_minimum_size", Vector2.ZERO, 0.3)
await height_tween.finished await height_tween.finished
owner.on_sticky_panel_cleared(get_parent().get_children().find(self)) var board = _get_board()
if board:
board.on_sticky_panel_cleared(get_parent().get_children().find(self))
self.queue_free() self.queue_free()
func replace_sticky_note_with(new_sticky_note: StickyNote): func replace_sticky_note_with(new_sticky_note: StickyNote):
@ -113,13 +111,19 @@ func can_accept_drop(draggable: Draggable) -> bool:
func handle_drop(draggable: StickyNote) -> int: func handle_drop(draggable: StickyNote) -> int:
if not can_accept_drop(draggable): if not can_accept_drop(draggable):
return Draggable.DropResult.REJECTED return Draggable.DropResult.REJECTED
# Attach sticky to this panel with animation # Attach sticky to this panel with animation
attatch_sticky_note(draggable, owner, true) var board = _get_board()
if board:
attatch_sticky_note(draggable, board, true)
# Clean up other empty panels # Clean up other empty panels
for panel in get_parent().get_children(): for panel in get_parent().get_children():
if panel is StickyNotePanel and panel != self: if panel is StickyNotePanel and panel != self:
panel.clear_if_empty() panel.clear_if_empty()
return Draggable.DropResult.ACCEPTED return Draggable.DropResult.ACCEPTED
func _get_board() -> CardBoard:
return get_parent().get_parent() as CardBoard