fix: card-board uses more single-responsibility, and now allows cards to slide nicely into the gaps
This commit is contained in:
parent
666df45e06
commit
b47801ae5c
|
|
@ -89,16 +89,28 @@ func _smooth(current: Vector2, goal: Vector2, delta: float) -> Vector2:
|
|||
func _process(delta: float):
|
||||
var zone_position := Vector2(notezone.get_screen_position().x + sticky_width / 3.0, sticky_height)
|
||||
|
||||
var dragging := notes.any(func (n : Draggable): return n.is_dragged)
|
||||
|
||||
if dragging:
|
||||
# Y-sort the nodes, this lets us fill the gap more nicely.
|
||||
notes.sort_custom(func (a:Draggable, b:Draggable): return a.global_position.y < b.global_position.y)
|
||||
|
||||
for note in notes:
|
||||
# Skip all dragged and already attached notes
|
||||
if note.is_attached: continue
|
||||
if note.is_dragged: continue
|
||||
|
||||
# Magnetically move all notes to where they ought to be on screen
|
||||
note.position = _smooth(note.position, zone_position, delta)
|
||||
note.home = zone_position
|
||||
zone_position.y += sticky_height
|
||||
|
||||
pass
|
||||
# Only if not already in transit / animated or user holding on to one
|
||||
if not dragging and not note.tween:
|
||||
note.animate_home()
|
||||
else:
|
||||
# do adjustment with FIR filter
|
||||
note.position = _smooth(note.position, note.home, delta)
|
||||
|
||||
|
||||
|
||||
func _check_completion() -> void:
|
||||
|
|
@ -195,7 +207,7 @@ func add_card(card: Card) -> void:
|
|||
func add_note(note: StickyNote) -> void:
|
||||
add_child(note)
|
||||
notes.append(note)
|
||||
note.is_dragable = true
|
||||
note.is_draggable = true
|
||||
|
||||
func appear():
|
||||
await Main.curtain.close()
|
||||
|
|
@ -250,30 +262,15 @@ func _end_drag(draggable: Draggable) -> void:
|
|||
if destination and destination is Card:
|
||||
var target_card := destination as Card
|
||||
|
||||
# If sticky was previously attached to a different card, detach it first
|
||||
if sticky.is_attached and sticky.attached_to != target_card:
|
||||
sticky.attached_to.remove_sticky_note()
|
||||
sticky.attached_to.remove_note_if_present()
|
||||
|
||||
# If target card already has a sticky, exchange them
|
||||
if target_card.has_sticky_note_attached():
|
||||
var exchanged_sticky := target_card.exchange_sticky_note_with(sticky)
|
||||
# Reclaim the exchanged sticky to the board
|
||||
if exchanged_sticky:
|
||||
reclaim_sticky(exchanged_sticky)
|
||||
else:
|
||||
# Simple attach
|
||||
sticky.reparent(target_card)
|
||||
target_card.attach_sticky_note(sticky)
|
||||
target_card.attach_or_exchange_note(sticky)
|
||||
|
||||
# If dropped on board (no destination), ensure it's a child of the board
|
||||
elif not destination:
|
||||
# If it was attached to a card, detach it first
|
||||
if sticky.is_attached:
|
||||
sticky.attached_to.remove_sticky_note()
|
||||
|
||||
# Make sure sticky is parented to board
|
||||
if sticky.get_parent() != self:
|
||||
sticky.reparent(self)
|
||||
reclaim_sticky(sticky)
|
||||
|
||||
# Check win condition after any sticky movement
|
||||
check_board_completion()
|
||||
|
|
@ -281,6 +278,7 @@ func _end_drag(draggable: Draggable) -> void:
|
|||
|
||||
func reclaim_sticky(note: StickyNote):
|
||||
note.reparent(self)
|
||||
note.tween = null
|
||||
|
||||
|
||||
func check_board_completion():
|
||||
|
|
@ -305,8 +303,8 @@ func give_lore_feedback():
|
|||
|
||||
for child in dropzone.get_children():
|
||||
if child is Card:
|
||||
if child.has_sticky_note_attached():
|
||||
fitting_card_count += int(child.card_id == child.get_attached_sticky_note().parent_id)
|
||||
if child.has_note_attached():
|
||||
fitting_card_count += int(child.card_id == child.get_attached_note().parent_id)
|
||||
total_card_count += 1
|
||||
|
||||
if float(fitting_card_count) / float(total_card_count) < 0.2:
|
||||
|
|
@ -421,7 +419,7 @@ func save_to_resource(savegame: SaveGame) -> void:
|
|||
print_debug(" Card '%s' at %s" % [card.name, card.position])
|
||||
|
||||
# Save sticky note attachment if present
|
||||
var note: StickyNote = card.get_attached_sticky_note()
|
||||
var note: StickyNote = card.get_attached_note()
|
||||
if note:
|
||||
savegame.board_attachments[note.name] = card.name
|
||||
print_debug(" Sticky '%s' attached to card '%s'" % [note.name, card.name])
|
||||
|
|
@ -496,8 +494,7 @@ func initialise_from_save(savegame: SaveGame) -> void:
|
|||
if card_name and cards_by_name.has(card_name):
|
||||
# Sticky is attached to a card
|
||||
var card: Card = cards_by_name[card_name]
|
||||
sticky.reparent(card)
|
||||
card.attach_sticky_note(sticky)
|
||||
card.attach_or_exchange_note(sticky, true)
|
||||
print_debug(" Sticky '%s' attached to card '%s'" % [sticky.name, card_name])
|
||||
else:
|
||||
# Sticky is loose on the board
|
||||
|
|
|
|||
|
|
@ -203,10 +203,10 @@ func _move_card():
|
|||
if is_dragged:
|
||||
update_drag_position(get_viewport().get_mouse_position())
|
||||
|
||||
func has_sticky_note_attached() -> bool:
|
||||
return get_attached_sticky_note() != null
|
||||
func has_note_attached() -> bool:
|
||||
return get_attached_note() != null
|
||||
|
||||
func get_attached_sticky_note() -> StickyNote:
|
||||
func get_attached_note() -> StickyNote:
|
||||
for child in get_children(false):
|
||||
if child is StickyNote:
|
||||
return child
|
||||
|
|
@ -224,41 +224,40 @@ func preview_sticky_note(sticky_note: StickyNote):
|
|||
else:
|
||||
push_warning("Card.preview_sticky_note: Invalid position calculated, skipping tween")
|
||||
|
||||
func attach_sticky_note(sticky_note: StickyNote) -> bool:
|
||||
if has_sticky_note_attached():
|
||||
return false
|
||||
|
||||
sticky_note.reparent(self)
|
||||
sticky_note.position = sticky_note_position
|
||||
sticky_note.is_dragable = false
|
||||
func attach_or_exchange_note(note: StickyNote, instant: bool = false) -> void:
|
||||
prints("Attaching", note, "to", self)
|
||||
|
||||
if name == "c_hit" and sticky_note.name == "c_effort" and Steamworks.has_initialized:
|
||||
# Out with the old...
|
||||
remove_note_if_present()
|
||||
|
||||
# ... in with the new
|
||||
note.reparent(self)
|
||||
note.home = sticky_note_position
|
||||
|
||||
if not instant:
|
||||
note.animate_home()
|
||||
else:
|
||||
note.position = sticky_note_position
|
||||
|
||||
if name == "c_hit" and note.name == "c_effort" and Steamworks.has_initialized:
|
||||
Steam.setAchievement("FIGHT_FOR_GOOD")
|
||||
Steam.storeStats()
|
||||
|
||||
return true
|
||||
|
||||
func remove_sticky_note() -> StickyNote:
|
||||
var former_child: StickyNote = get_attached_sticky_note()
|
||||
if not former_child:
|
||||
return null
|
||||
func remove_note_if_present() -> void:
|
||||
var former_child: StickyNote = get_attached_note()
|
||||
if not former_child: return
|
||||
|
||||
former_child.reparent(get_parent())
|
||||
return former_child
|
||||
|
||||
func exchange_sticky_note_with(new_note: StickyNote) -> StickyNote:
|
||||
if new_note == get_attached_sticky_note():
|
||||
return null
|
||||
|
||||
var old_note := remove_sticky_note()
|
||||
attach_sticky_note(new_note)
|
||||
return old_note
|
||||
former_child.tween = null # the positioning logic in card-board will pick that one up and calc a nice slot.
|
||||
|
||||
|
||||
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
||||
|
||||
## Checks if this card can accept the given draggable
|
||||
func can_accept_drop(draggable: Draggable) -> bool:
|
||||
return draggable is StickyNote and draggable != self
|
||||
return draggable is StickyNote
|
||||
|
||||
## Handles dropping a sticky note onto this card
|
||||
## Returns DropResult indicating success, rejection, or exchange
|
||||
|
|
@ -266,22 +265,9 @@ func handle_drop(draggable: StickyNote) -> int:
|
|||
if not can_accept_drop(draggable):
|
||||
return Draggable.DropResult.REJECTED
|
||||
|
||||
if has_sticky_note_attached():
|
||||
# Exchange: remove current, attach new, store old for retrieval
|
||||
exchange_sticky_note_with(draggable)
|
||||
# Reset z_index for newly attached sticky
|
||||
draggable.z_index = 0
|
||||
return Draggable.DropResult.ACCEPTED
|
||||
else:
|
||||
# Simple attach
|
||||
if attach_sticky_note(draggable):
|
||||
# Reset z_index for newly attached sticky
|
||||
draggable.z_index = 0
|
||||
return Draggable.DropResult.ACCEPTED
|
||||
else:
|
||||
# Attach failed (shouldn't happen, but handle it)
|
||||
return Draggable.DropResult.REJECTED
|
||||
|
||||
attach_or_exchange_note(draggable)
|
||||
draggable.z_index = 0
|
||||
return Draggable.DropResult.ACCEPTED
|
||||
|
||||
# === DRAG LIFECYCLE OVERRIDES ===
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ var is_dragged: bool = false:
|
|||
is_dragged = dragged
|
||||
z_index = int(dragged)
|
||||
|
||||
# local coordinates home position, where the draggable can try to animate_home to
|
||||
var home : Vector2 = Vector2.ZERO
|
||||
|
||||
## Internal highlighted state - do not set directly, use set_highlight()
|
||||
var _highlighted: bool = false
|
||||
|
||||
|
|
@ -63,6 +66,13 @@ func _get_board() -> Node:
|
|||
|
||||
## === DRAG LIFECYCLE METHODS ===
|
||||
## Override these in Card and StickyNote for specific behavior
|
||||
var tween : Tween = null
|
||||
|
||||
|
||||
func animate_home() -> void:
|
||||
if tween: tween.kill()
|
||||
tween = create_tween().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_BACK)
|
||||
tween.tween_property(self, "position", home, 0.5)
|
||||
|
||||
func _on_mouse_entered() -> void:
|
||||
#prints("Draggable[base]._on_mouse_entered", self, self.name)
|
||||
|
|
|
|||
|
|
@ -58,19 +58,13 @@ func set_highlight(value: bool) -> void:
|
|||
shift_tween.tween_property(content, "position", Vector2.ZERO, 0.5)
|
||||
|
||||
@export var voice_line: AudioStream = null
|
||||
@export var is_dragable: bool = false
|
||||
@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
|
||||
|
||||
## 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:
|
||||
name = sticky_name
|
||||
|
|
@ -92,45 +86,9 @@ func _on_text_updated():
|
|||
background_sprite.frame = text.hash() % background_sprite.sprite_frames.get_frame_count(background_sprite.animation)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
func _move_sticky_note(delta: float) -> void:
|
||||
func _process(_delta: float) -> void:
|
||||
if is_dragged:
|
||||
update_drag_position(get_viewport().get_mouse_position())
|
||||
return
|
||||
|
||||
if is_attached:
|
||||
var card := attached_to
|
||||
position = _smooth(position, card.sticky_note_position, delta)
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
if transform_tween and transform_tween.is_running():
|
||||
transform_tween.stop()
|
||||
|
||||
transform_tween = create_tween()
|
||||
transform_tween.tween_property(self, "transform", target, duration)
|
||||
|
||||
await transform_tween.finished
|
||||
transform_tween_finished.emit()
|
||||
|
||||
|
||||
# === DRAG LIFECYCLE OVERRIDES ===
|
||||
|
|
|
|||
Loading…
Reference in New Issue