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):
|
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)
|
||||||
|
|
||||||
|
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:
|
for note in notes:
|
||||||
# Skip all dragged and already attached notes
|
# Skip all dragged and already attached notes
|
||||||
if note.is_attached: continue
|
if note.is_attached: continue
|
||||||
if note.is_dragged: continue
|
if note.is_dragged: continue
|
||||||
|
|
||||||
# Magnetically move all notes to where they ought to be on screen
|
# 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
|
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:
|
func _check_completion() -> void:
|
||||||
|
|
@ -195,7 +207,7 @@ func add_card(card: Card) -> void:
|
||||||
func add_note(note: StickyNote) -> void:
|
func add_note(note: StickyNote) -> void:
|
||||||
add_child(note)
|
add_child(note)
|
||||||
notes.append(note)
|
notes.append(note)
|
||||||
note.is_dragable = true
|
note.is_draggable = true
|
||||||
|
|
||||||
func appear():
|
func appear():
|
||||||
await Main.curtain.close()
|
await Main.curtain.close()
|
||||||
|
|
@ -250,30 +262,15 @@ func _end_drag(draggable: Draggable) -> void:
|
||||||
if destination and destination is Card:
|
if destination and destination is Card:
|
||||||
var target_card := destination as 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:
|
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
|
target_card.attach_or_exchange_note(sticky)
|
||||||
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)
|
|
||||||
|
|
||||||
# If dropped on board (no destination), ensure it's a child of the board
|
# If dropped on board (no destination), ensure it's a child of the board
|
||||||
elif not destination:
|
elif not destination:
|
||||||
# If it was attached to a card, detach it first
|
|
||||||
if sticky.is_attached:
|
if sticky.is_attached:
|
||||||
sticky.attached_to.remove_sticky_note()
|
reclaim_sticky(sticky)
|
||||||
|
|
||||||
# Make sure sticky is parented to board
|
|
||||||
if sticky.get_parent() != self:
|
|
||||||
sticky.reparent(self)
|
|
||||||
|
|
||||||
# Check win condition after any sticky movement
|
# Check win condition after any sticky movement
|
||||||
check_board_completion()
|
check_board_completion()
|
||||||
|
|
@ -281,6 +278,7 @@ func _end_drag(draggable: Draggable) -> void:
|
||||||
|
|
||||||
func reclaim_sticky(note: StickyNote):
|
func reclaim_sticky(note: StickyNote):
|
||||||
note.reparent(self)
|
note.reparent(self)
|
||||||
|
note.tween = null
|
||||||
|
|
||||||
|
|
||||||
func check_board_completion():
|
func check_board_completion():
|
||||||
|
|
@ -305,8 +303,8 @@ func give_lore_feedback():
|
||||||
|
|
||||||
for child in dropzone.get_children():
|
for child in dropzone.get_children():
|
||||||
if child is Card:
|
if child is Card:
|
||||||
if child.has_sticky_note_attached():
|
if child.has_note_attached():
|
||||||
fitting_card_count += int(child.card_id == child.get_attached_sticky_note().parent_id)
|
fitting_card_count += int(child.card_id == child.get_attached_note().parent_id)
|
||||||
total_card_count += 1
|
total_card_count += 1
|
||||||
|
|
||||||
if float(fitting_card_count) / float(total_card_count) < 0.2:
|
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])
|
print_debug(" Card '%s' at %s" % [card.name, card.position])
|
||||||
|
|
||||||
# Save sticky note attachment if present
|
# Save sticky note attachment if present
|
||||||
var note: StickyNote = card.get_attached_sticky_note()
|
var note: StickyNote = card.get_attached_note()
|
||||||
if note:
|
if note:
|
||||||
savegame.board_attachments[note.name] = card.name
|
savegame.board_attachments[note.name] = card.name
|
||||||
print_debug(" Sticky '%s' attached to card '%s'" % [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):
|
if card_name and cards_by_name.has(card_name):
|
||||||
# Sticky is attached to a card
|
# Sticky is attached to a card
|
||||||
var card: Card = cards_by_name[card_name]
|
var card: Card = cards_by_name[card_name]
|
||||||
sticky.reparent(card)
|
card.attach_or_exchange_note(sticky, true)
|
||||||
card.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])
|
||||||
else:
|
else:
|
||||||
# Sticky is loose on the board
|
# Sticky is loose on the board
|
||||||
|
|
|
||||||
|
|
@ -203,10 +203,10 @@ func _move_card():
|
||||||
if is_dragged:
|
if is_dragged:
|
||||||
update_drag_position(get_viewport().get_mouse_position())
|
update_drag_position(get_viewport().get_mouse_position())
|
||||||
|
|
||||||
func has_sticky_note_attached() -> bool:
|
func has_note_attached() -> bool:
|
||||||
return get_attached_sticky_note() != null
|
return get_attached_note() != null
|
||||||
|
|
||||||
func get_attached_sticky_note() -> StickyNote:
|
func get_attached_note() -> StickyNote:
|
||||||
for child in get_children(false):
|
for child in get_children(false):
|
||||||
if child is StickyNote:
|
if child is StickyNote:
|
||||||
return child
|
return child
|
||||||
|
|
@ -224,41 +224,40 @@ func preview_sticky_note(sticky_note: StickyNote):
|
||||||
else:
|
else:
|
||||||
push_warning("Card.preview_sticky_note: Invalid position calculated, skipping tween")
|
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)
|
func attach_or_exchange_note(note: StickyNote, instant: bool = false) -> void:
|
||||||
sticky_note.position = sticky_note_position
|
prints("Attaching", note, "to", self)
|
||||||
sticky_note.is_dragable = false
|
|
||||||
|
|
||||||
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.setAchievement("FIGHT_FOR_GOOD")
|
||||||
Steam.storeStats()
|
Steam.storeStats()
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
func remove_sticky_note() -> StickyNote:
|
func remove_note_if_present() -> void:
|
||||||
var former_child: StickyNote = get_attached_sticky_note()
|
var former_child: StickyNote = get_attached_note()
|
||||||
if not former_child:
|
if not former_child: return
|
||||||
return null
|
|
||||||
former_child.reparent(get_parent())
|
former_child.reparent(get_parent())
|
||||||
return former_child
|
former_child.tween = null # the positioning logic in card-board will pick that one up and calc a nice slot.
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
||||||
|
|
||||||
## Checks if this card can accept the given draggable
|
## Checks if this card can accept the given draggable
|
||||||
func can_accept_drop(draggable: Draggable) -> bool:
|
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
|
## Handles dropping a sticky note onto this card
|
||||||
## Returns DropResult indicating success, rejection, or exchange
|
## Returns DropResult indicating success, rejection, or exchange
|
||||||
|
|
@ -266,22 +265,9 @@ 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
|
||||||
|
|
||||||
if has_sticky_note_attached():
|
attach_or_exchange_note(draggable)
|
||||||
# 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
|
draggable.z_index = 0
|
||||||
return Draggable.DropResult.ACCEPTED
|
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
|
|
||||||
|
|
||||||
|
|
||||||
# === DRAG LIFECYCLE OVERRIDES ===
|
# === DRAG LIFECYCLE OVERRIDES ===
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@ var is_dragged: bool = false:
|
||||||
is_dragged = dragged
|
is_dragged = dragged
|
||||||
z_index = int(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()
|
## Internal highlighted state - do not set directly, use set_highlight()
|
||||||
var _highlighted: bool = false
|
var _highlighted: bool = false
|
||||||
|
|
||||||
|
|
@ -63,6 +66,13 @@ func _get_board() -> Node:
|
||||||
|
|
||||||
## === DRAG LIFECYCLE METHODS ===
|
## === DRAG LIFECYCLE METHODS ===
|
||||||
## Override these in Card and StickyNote for specific behavior
|
## 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:
|
func _on_mouse_entered() -> void:
|
||||||
#prints("Draggable[base]._on_mouse_entered", self, self.name)
|
#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)
|
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_draggable: bool = false
|
||||||
|
|
||||||
var mouse_offset: Vector2
|
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
|
||||||
|
|
||||||
## 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
|
||||||
|
|
@ -92,45 +86,9 @@ func _on_text_updated():
|
||||||
background_sprite.frame = text.hash() % background_sprite.sprite_frames.get_frame_count(background_sprite.animation)
|
background_sprite.frame = text.hash() % background_sprite.sprite_frames.get_frame_count(background_sprite.animation)
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
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:
|
|
||||||
if is_dragged:
|
if is_dragged:
|
||||||
update_drag_position(get_viewport().get_mouse_position())
|
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 ===
|
# === DRAG LIFECYCLE OVERRIDES ===
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue