WIP: implement sideboard with scrolling and capability to accept both card and sticky drops depending on context
This commit is contained in:
parent
af7411a876
commit
aca35b34a0
|
|
@ -20,20 +20,46 @@ var cards : Array[Card] = []
|
|||
|
||||
var board_was_completed: bool = false
|
||||
|
||||
var current_context : int = NAVIGATE
|
||||
var current_context : Context = Context.NAVIGATE
|
||||
var selection_state : SelectionState
|
||||
|
||||
var last_card_selected: Card
|
||||
var last_note_selected: StickyNote
|
||||
var selection: Draggable = null:
|
||||
set(value):
|
||||
if selection == value: return
|
||||
|
||||
if selection:
|
||||
if selection is Card:
|
||||
last_card_selected = selection
|
||||
elif selection is StickyNote:
|
||||
last_note_selected = selection
|
||||
|
||||
# Select & highlight new
|
||||
if selection: selection.highlighted = false
|
||||
selection = value
|
||||
if selection: selection.highlighted = true
|
||||
|
||||
# Are we selecting cards or stickies?
|
||||
if selection is Card:
|
||||
selection_state = SelectionState.CARDS
|
||||
if current_context == Context.ASSIGN:
|
||||
selection.preview_sticky_note(last_note_selected)
|
||||
|
||||
if selection is StickyNote:
|
||||
selection_state = SelectionState.STICKIES
|
||||
|
||||
#@onready var instructions := $instructions_panel/HBoxContainer/cards_remaining
|
||||
|
||||
@onready var dropzone : Control = %CardZone
|
||||
@onready var notezone : Control = %NoteZone
|
||||
@onready var sideboard : SideBoard = %SideBoard
|
||||
|
||||
enum SelectionState {FREE,STICKIES,CARDS}
|
||||
enum {NAVIGATE, ASSIGN, DRAG}
|
||||
enum Context {NAVIGATE, ASSIGN, DRAG}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
prints("card-board.gd:", "_ready()", self, "room:", State.room, owner)
|
||||
prints("card-board.gd:", "_ready()", self, "room:", State.active_room, owner)
|
||||
super._ready()
|
||||
|
||||
_delayed_ready.call_deferred()
|
||||
|
|
@ -42,9 +68,10 @@ func _ready() -> void:
|
|||
# We need to wait for our room further up our parent hierarchy to actually make itself known
|
||||
# for interactables to do things with it, e.g. CardBoard needs to register itself
|
||||
func _delayed_ready() ->void:
|
||||
var board_room := State.room as RoomWithBoard
|
||||
assert(board_room, "CardBoard spawned in room that's not a RoomWithboard.")
|
||||
board_room.card_board = self
|
||||
var board_room := State.active_room as RoomWithBoard
|
||||
if not get_tree().root == get_parent():
|
||||
assert(board_room, "CardBoard spawned in room that's not a RoomWithboard.")
|
||||
board_room.card_board = self
|
||||
is_memory_board = is_memory_board
|
||||
|
||||
|
||||
|
|
@ -75,23 +102,6 @@ var mementos_collected: int = 0:
|
|||
mementos_collected = mementos
|
||||
|
||||
|
||||
var selection: Draggable = null:
|
||||
set(value):
|
||||
if selection == value: return
|
||||
|
||||
# Select & highlight new
|
||||
if selection: selection.highlighted = false
|
||||
selection = value
|
||||
if selection: selection.highlighted = true
|
||||
|
||||
# Are we selecting cards or stickies?
|
||||
if selection is Card:
|
||||
selection_state = SelectionState.CARDS
|
||||
|
||||
if selection is StickyNote:
|
||||
selection_state = SelectionState.STICKIES
|
||||
|
||||
|
||||
func _navigate_next():
|
||||
var candidates := _selection_candidates
|
||||
var index := maxi(0, candidates.find(selection))
|
||||
|
|
@ -111,34 +121,6 @@ func _smooth(current: Vector2, goal: Vector2, delta: float) -> Vector2:
|
|||
return (k) * current + (1.0-k) * goal
|
||||
|
||||
|
||||
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.home = zone_position
|
||||
zone_position.y += sticky_height
|
||||
|
||||
# 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)
|
||||
note.z_index = 0
|
||||
|
||||
|
||||
|
||||
func _check_completion() -> void:
|
||||
if is_board_complete():
|
||||
board_was_completed = true
|
||||
|
|
@ -148,7 +130,7 @@ func _check_completion() -> void:
|
|||
## Finalizes board state before closing (ends drags, cleans up transitions)
|
||||
func _finalize_board_state() -> void:
|
||||
# End any active drag operations
|
||||
if current_context == DRAG:
|
||||
if current_context == Context.DRAG:
|
||||
_end_drag(selection)
|
||||
for item in notes:
|
||||
item.is_dragged = false
|
||||
|
|
@ -157,7 +139,7 @@ func _finalize_board_state() -> void:
|
|||
item.is_dragged = false
|
||||
|
||||
# Reset context to NAVIGATE
|
||||
current_context = NAVIGATE
|
||||
current_context = Context.NAVIGATE
|
||||
print("CardBoard: Board state finalized")
|
||||
|
||||
|
||||
|
|
@ -181,8 +163,8 @@ func populate_board(names: Array[StringName]):
|
|||
for new_card: Card in all_new["cards"]:
|
||||
add_card(new_card)
|
||||
|
||||
for new_sticky_note: StickyNote in all_new["sticky_notes"]:
|
||||
add_note(new_sticky_note)
|
||||
|
||||
notes += sideboard.populate(all_new["sticky_notes"])
|
||||
|
||||
|
||||
# FIXME: This can be made even simpler.
|
||||
|
|
@ -243,14 +225,14 @@ func add_note(note: StickyNote) -> void:
|
|||
note.is_draggable = true
|
||||
|
||||
func appear():
|
||||
await Main.curtain.close()
|
||||
#await Main.curtain.close()
|
||||
show()
|
||||
await Main.curtain.open()
|
||||
#await Main.curtain.open()
|
||||
|
||||
func vanish():
|
||||
await Main.curtain.close()
|
||||
#await Main.curtain.close()
|
||||
hide()
|
||||
await Main.curtain.open()
|
||||
#await Main.curtain.open()
|
||||
|
||||
|
||||
# Called by notes when a mouse event needs handling
|
||||
|
|
@ -265,13 +247,15 @@ func handle_mouse_button(input: InputEventMouseButton, target: Draggable) -> voi
|
|||
_end_drag(target)
|
||||
return
|
||||
|
||||
|
||||
var parent_before_drag: Node
|
||||
## Starts a drag operation for the given draggable
|
||||
func _start_drag(draggable: Draggable) -> void:
|
||||
parent_before_drag = draggable.get_parent()
|
||||
selection = draggable
|
||||
current_context = DRAG
|
||||
current_context = Context.DRAG
|
||||
var mouse_offset := get_viewport().get_mouse_position() - draggable.global_position
|
||||
draggable.start_drag(mouse_offset)
|
||||
draggable.reparent(self)
|
||||
|
||||
|
||||
## Ends a drag operation and handles the drop
|
||||
|
|
@ -279,26 +263,30 @@ func _end_drag(draggable: Draggable) -> void:
|
|||
selection = draggable
|
||||
|
||||
# Cleanup and state update
|
||||
current_context = NAVIGATE
|
||||
current_context = Context.NAVIGATE
|
||||
|
||||
var destination := draggable.end_drag()
|
||||
|
||||
if destination:
|
||||
if destination is Card:
|
||||
# If dropped on a card, attach it
|
||||
if draggable is StickyNote:
|
||||
destination.attach_or_exchange_note(draggable)
|
||||
|
||||
elif destination is SideBoundary:
|
||||
if not sideboard.accept_drop(draggable):
|
||||
#FIXME remove if handle_drop and can_accept_drop is properly implemented.
|
||||
destination = null
|
||||
|
||||
# If dropped on board (no destination), ensure it's a child of the board
|
||||
if not destination:
|
||||
if draggable is StickyNote:
|
||||
reclaim_sticky(draggable)
|
||||
|
||||
# Handle sticky note drops
|
||||
if draggable is StickyNote:
|
||||
var sticky := draggable as StickyNote
|
||||
# Check win condition after any sticky movement
|
||||
check_board_completion()
|
||||
|
||||
# If dropped on a card, attach it
|
||||
if destination and destination is Card:
|
||||
var target_card := destination as Card
|
||||
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 sticky.is_attached:
|
||||
reclaim_sticky(sticky)
|
||||
|
||||
# Check win condition after any sticky movement
|
||||
check_board_completion()
|
||||
parent_before_drag = null
|
||||
|
||||
|
||||
func reclaim_sticky(note: StickyNote):
|
||||
|
|
@ -384,8 +372,6 @@ func _nearest_hovered(candidates: Array[Draggable]) -> Draggable:
|
|||
return null
|
||||
|
||||
|
||||
|
||||
|
||||
func _by_spatial(a: Draggable, b: Draggable) -> bool:
|
||||
return a.position.x + a.position.y * 10000 > b.position.x + b.position.y * 10000
|
||||
|
||||
|
|
@ -408,6 +394,7 @@ func _sort_by_positions() -> void:
|
|||
func can_accept_drop(draggable: Draggable) -> bool:
|
||||
return draggable is Card or draggable is StickyNote
|
||||
|
||||
# FIXME: this function seems to not be used.
|
||||
## Handles dropping a draggable onto the board (into the dropzone)
|
||||
func handle_drop(draggable: Draggable) -> int:
|
||||
if not can_accept_drop(draggable):
|
||||
|
|
@ -423,12 +410,49 @@ func _perform(action : StringName) -> void:
|
|||
|
||||
# Takes the inputs for control inputs
|
||||
func _input(event : InputEvent) -> void:
|
||||
if selection and not selection.is_dragged and event is InputEventMouseMotion and not event.is_action_pressed("mouse_left"):
|
||||
# trying to not do the double check on the line below.
|
||||
var is_mouse_left:= false
|
||||
if event is InputEventMouseButton:
|
||||
is_mouse_left = event.button_index == MOUSE_BUTTON_LEFT
|
||||
|
||||
if selection and not selection.is_dragged and event is InputEventMouseMotion and not is_mouse_left:
|
||||
var candidate := _nearest_hovered(_sort_by_proximity_and_depth(notes))
|
||||
if not candidate:
|
||||
candidate = _nearest_hovered(_sort_by_proximity_and_depth(cards))
|
||||
selection = candidate
|
||||
if event.is_action_pressed("ui_left"):
|
||||
_handle_direction_input(Vector2.LEFT)
|
||||
if event.is_action_pressed("ui_right"):
|
||||
_handle_direction_input(Vector2.RIGHT)
|
||||
if event.is_action_pressed("ui_up"):
|
||||
_handle_direction_input(Vector2.UP)
|
||||
if event.is_action_pressed("ui_down"):
|
||||
_handle_direction_input(Vector2.DOWN)
|
||||
|
||||
#TODO: make this work with the prompter once more.
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
get_viewport().set_input_as_handled()
|
||||
closed.emit()
|
||||
|
||||
func _handle_direction_input(direction: Vector2):
|
||||
|
||||
if current_context == Context.NAVIGATE and _assure_selection():
|
||||
try_select_nearest_card(selection.global_position, direction, (current_context != Context.ASSIGN))
|
||||
|
||||
func _assure_selection() -> bool:
|
||||
if selection:
|
||||
return true
|
||||
else:
|
||||
if selection_state == SelectionState.STICKIES or selection_state == SelectionState.FREE:
|
||||
if dropzone.get_child_count() > 0:
|
||||
selection = dropzone.get_child(0)
|
||||
return true
|
||||
## Attempting to still select a Card if no sticky could be found.
|
||||
if sideboard.get_child_count() > 0:
|
||||
selection = sideboard.get_child(0)
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
## Saves board state directly to SaveGame resource
|
||||
func save_to_resource(savegame: SaveGame) -> void:
|
||||
|
|
@ -572,3 +596,65 @@ func _debug_mode() -> void:
|
|||
populate_board(["c_out_of_world", 'c_confusion', "p_outer_conflict", "p_unique"])
|
||||
await get_tree().process_frame
|
||||
play()
|
||||
|
||||
|
||||
|
||||
func try_select_nearest_card(from: Vector2, towards: Vector2, include_stickies: bool = false) -> bool:
|
||||
var selection_transform = Transform2D(0, from).looking_at(from+towards)
|
||||
|
||||
var scores: Dictionary[int, Area2D] = {-1: null}
|
||||
for child:Area2D in dropzone.get_children():
|
||||
if not (child is StickyNote and include_stickies):
|
||||
scores[get_distance_score(child.global_position, selection_transform)] = child
|
||||
scores.erase(-1)
|
||||
scores.sort()
|
||||
|
||||
if include_stickies:
|
||||
var panel_scores: Dictionary[int, Control] = {-1: null}
|
||||
for child:Control in sideboard.get_children():
|
||||
if not child.is_empty():
|
||||
panel_scores[get_distance_score(child.attached_sticky_note.global_position, selection_transform)] = child
|
||||
panel_scores.erase(-1)
|
||||
panel_scores.sort()
|
||||
|
||||
if panel_scores != {}:
|
||||
if scores != {}:
|
||||
if panel_scores.keys()[0] < scores.keys()[0]:
|
||||
if current_context == Context.ASSIGN: return false
|
||||
selection = panel_scores.values()[0]
|
||||
selection_state = SelectionState.STICKIES
|
||||
return true
|
||||
else:
|
||||
if current_context == Context.ASSIGN: return false
|
||||
selection = panel_scores.values()[0]
|
||||
selection_state = SelectionState.STICKIES
|
||||
return true
|
||||
|
||||
|
||||
if scores != {}:
|
||||
selection = scores.values()[0]
|
||||
return true
|
||||
return false
|
||||
|
||||
func try_select_nearest_empty_card(from: Vector2) -> bool:
|
||||
var scores: Dictionary[int, Area2D] = {}
|
||||
|
||||
for card in dropzone.get_children():
|
||||
if card is Card:
|
||||
if not card.has_note_attached():
|
||||
scores[int((from-card.global_position).length())] = card
|
||||
|
||||
scores.sort()
|
||||
|
||||
if scores != {}:
|
||||
selection = scores.values()[0]
|
||||
return true
|
||||
return false
|
||||
|
||||
func get_distance_score(from: Vector2, to: Transform2D) -> int:
|
||||
var diff = from * to
|
||||
var dir = diff.normalized()
|
||||
if dir.x > 0.5 and diff.length() > 0:
|
||||
return int((abs(dir.y) + 0.5) * diff.length())
|
||||
else:
|
||||
return -1
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ var transfor_arr: Array[Transform2D] = [
|
|||
@export_range(1, 2) var scale_bump: float = 1.05
|
||||
@export_range(1.0, 10.0) var bounce_speed: float = 5
|
||||
|
||||
@export_color_no_alpha var highlight_color: Color = Color(1.4, 1.4, 1.4)
|
||||
@export_color_no_alpha var highlight_color: Color = Color(1.2, 1.2, 1.2)
|
||||
|
||||
## Override set_highlight to add visual feedback for cards
|
||||
func set_highlight(value: bool) -> void:
|
||||
|
|
@ -231,6 +231,10 @@ func get_attached_note() -> StickyNote:
|
|||
return null
|
||||
|
||||
|
||||
func get_size() -> Vector2:
|
||||
return $CollisionShape2D.shape.size
|
||||
|
||||
|
||||
func preview_sticky_note(sticky_note: StickyNote):
|
||||
if not is_instance_valid(sticky_note):
|
||||
return
|
||||
|
|
@ -252,7 +256,7 @@ func attach_or_exchange_note(note: StickyNote, instant: bool = false) -> void:
|
|||
old.reparent(note.attached_to)
|
||||
old.animate_home()
|
||||
else:
|
||||
remove_note_if_present() # just kick out our old note
|
||||
return_old_child(note) # just kick out our old note
|
||||
|
||||
# ... in with the new
|
||||
note.reparent(self)
|
||||
|
|
@ -268,12 +272,18 @@ func attach_or_exchange_note(note: StickyNote, instant: bool = false) -> void:
|
|||
Steam.storeStats()
|
||||
|
||||
|
||||
func remove_note_if_present() -> void:
|
||||
func return_old_child(new_note: StickyNote = null) -> void:
|
||||
var former_child: StickyNote = get_attached_note()
|
||||
if not former_child: return
|
||||
|
||||
former_child.reparent(get_parent())
|
||||
|
||||
former_child.tween = null # the positioning logic in card-board will pick that one up and calc a nice slot.
|
||||
|
||||
if new_note:
|
||||
former_child.reparent(new_note.last_parent)
|
||||
former_child.animate_home(new_note.home)
|
||||
else:
|
||||
former_child.reparent(get_parent())
|
||||
|
||||
|
||||
|
||||
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
||||
|
|
@ -282,6 +292,7 @@ func remove_note_if_present() -> void:
|
|||
func can_accept_drop(draggable: Draggable) -> bool:
|
||||
return draggable is StickyNote
|
||||
|
||||
# FIXME: this function seems to not be used.
|
||||
## Handles dropping a sticky note onto this card
|
||||
## Returns DropResult indicating success, rejection, or exchange
|
||||
func handle_drop(draggable: StickyNote) -> int:
|
||||
|
|
@ -292,6 +303,23 @@ func handle_drop(draggable: StickyNote) -> int:
|
|||
draggable.z_index = 0
|
||||
return Draggable.DropResult.ACCEPTED
|
||||
|
||||
|
||||
## 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:
|
||||
print(get_overlapping_areas())
|
||||
# Priority 1: Check for overlapping cards in dropzone
|
||||
for area in get_overlapping_areas():
|
||||
if area is SideBoundary:
|
||||
return area
|
||||
|
||||
return null
|
||||
|
||||
# === DRAG LIFECYCLE OVERRIDES ===
|
||||
|
||||
## Cards always drop back to board dropzone
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ size = Vector2(277, 231)
|
|||
|
||||
[node name="Card" type="Area2D" unique_id=5263467]
|
||||
collision_layer = 4
|
||||
collision_mask = 0
|
||||
collision_mask = 8
|
||||
priority = 50
|
||||
script = ExtResource("1_emip0")
|
||||
text = "card"
|
||||
|
|
@ -34,6 +34,7 @@ offset_right = 136.0
|
|||
offset_bottom = 77.95834
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_default_cursor_shape = 13
|
||||
theme = ExtResource("3_mdi7r")
|
||||
theme_type_variation = &"card_text"
|
||||
text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ func set_highlight(value: bool) -> void:
|
|||
var _drag_start_position: Vector2
|
||||
var _mouse_drag_offset: Vector2
|
||||
var _drag_source: Node = null # Where the drag started from
|
||||
var last_parent: Node = null # Where they were last dragged from. Used by return_old_child() in Card
|
||||
|
||||
## === SETUP ###
|
||||
func _ready() -> void:
|
||||
|
|
@ -69,11 +70,15 @@ func _get_button_handler() -> Node:
|
|||
var tween : Tween = null
|
||||
|
||||
|
||||
func animate_home() -> void:
|
||||
func animate_home(target: Vector2 = home) -> void:
|
||||
z_index = 100
|
||||
if tween: tween.kill()
|
||||
tween = create_tween().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_QUART)
|
||||
tween.tween_property(self, "position", home, 0.5)
|
||||
tween.tween_property(self, "position", target, 0.5)
|
||||
|
||||
func get_size() -> Vector2:
|
||||
assert(false, "This function needs to be overridden!")
|
||||
return Vector2.ZERO
|
||||
|
||||
func _on_mouse_entered() -> void:
|
||||
#prints("Draggable[base]._on_mouse_entered", self, self.name)
|
||||
|
|
@ -110,6 +115,7 @@ func start_drag(mouse_offset: Vector2) -> void:
|
|||
_drag_start_position = global_position
|
||||
_mouse_drag_offset = mouse_offset
|
||||
_drag_source = get_parent()
|
||||
last_parent = _drag_source
|
||||
z_index = 60
|
||||
is_dragged = true
|
||||
|
||||
|
|
@ -132,6 +138,11 @@ func end_drag() -> Node:
|
|||
_drag_source = null
|
||||
return null
|
||||
|
||||
func _set_home() -> void:
|
||||
var parent = get_parent()
|
||||
if parent is Control:
|
||||
confine_to_screen()
|
||||
home = position
|
||||
|
||||
## Confines this draggable element to stay within screen or container bounds
|
||||
## Skip this check if a sticky note is attached to a card
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@
|
|||
[ext_resource type="Shader" uid="uid://kyd37e0s6fdu" path="res://logic-scenes/board/physics-board.gdshader" id="1_ggnth"]
|
||||
[ext_resource type="Script" uid="uid://cqsor57nvowni" path="res://logic-scenes/board/card-board.gd" id="3_8v4c4"]
|
||||
[ext_resource type="AudioStream" uid="uid://bywmf3patoe56" path="res://base-environments/youth_room/audio/board_completed.wav" id="5_qjqy3"]
|
||||
[ext_resource type="Script" uid="uid://cwc0k3mtj4k1f" path="res://logic-scenes/board/side_board.gd" id="6_kvxnu"]
|
||||
[ext_resource type="AudioStream" uid="uid://bgtohhyd8whbm" path="res://base-environments/youth_room/audio/board_completed_de.wav" id="6_ni75f"]
|
||||
[ext_resource type="AudioStream" uid="uid://dj8fpajqhj4k7" path="res://base-environments/youth_room/audio/board_incomplete.wav" id="6_vtvtf"]
|
||||
[ext_resource type="AudioStream" uid="uid://brolrc3lhaeid" path="res://base-environments/youth_room/audio/board_unfitting.wav" id="7_0phgc"]
|
||||
[ext_resource type="AudioStream" uid="uid://swlo6elqs4vx" path="res://base-environments/youth_room/audio/board_incomplete_de.wav" id="7_2qppy"]
|
||||
[ext_resource type="Script" uid="uid://c7gbr6rdk843f" path="res://logic-scenes/board/side_boundary.gd" id="7_k5h0q"]
|
||||
[ext_resource type="Script" uid="uid://c1oub0cs7cph6" path="res://dev-util/stereo-switch.gd" id="8_ni75f"]
|
||||
[ext_resource type="AudioStream" uid="uid://y8fg3wjscvci" path="res://base-environments/youth_room/audio/board_unfitting_de.wav" id="10_kvxnu"]
|
||||
[ext_resource type="Texture2D" uid="uid://diviesbhf6p77" path="res://logic-scenes/board/board-texture/cardbord-box.png" id="11_ni75f"]
|
||||
|
|
@ -20,6 +22,9 @@ shader_parameter/magic_scale_factor = 1500.0
|
|||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_m1g7s"]
|
||||
|
||||
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_htay1"]
|
||||
normal = Vector2(-1, 0)
|
||||
|
||||
[sub_resource type="Animation" id="Animation_qjqy3"]
|
||||
length = 0.001
|
||||
|
||||
|
|
@ -164,7 +169,6 @@ script = ExtResource("3_8v4c4")
|
|||
|
||||
[node name="CardboardBox" type="TextureRect" parent="." unique_id=450836053]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
clip_contents = true
|
||||
layout_direction = 3
|
||||
layout_mode = 2
|
||||
|
|
@ -173,8 +177,10 @@ expand_mode = 2
|
|||
|
||||
[node name="Label" type="Label" parent="CardboardBox" unique_id=1585163137]
|
||||
layout_mode = 0
|
||||
offset_right = 274.0
|
||||
offset_bottom = 99.416664
|
||||
offset_left = 14.0
|
||||
offset_top = 13.0
|
||||
offset_right = 288.0
|
||||
offset_bottom = 112.416664
|
||||
size_flags_horizontal = 8
|
||||
size_flags_vertical = 0
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
|
|
@ -192,11 +198,28 @@ layout_mode = 2
|
|||
size_flags_horizontal = 3
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="NoteZone" type="Control" parent="HBoxContainer" unique_id=92501430]
|
||||
[node name="SideBoard" type="ScrollContainer" parent="HBoxContainer" unique_id=738913213]
|
||||
unique_name_in_owner = true
|
||||
clip_contents = false
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
horizontal_scroll_mode = 0
|
||||
vertical_scroll_mode = 3
|
||||
script = ExtResource("6_kvxnu")
|
||||
|
||||
[node name="Panel" type="Control" parent="HBoxContainer/SideBoard" unique_id=1893108919]
|
||||
custom_minimum_size = Vector2(400, 1500)
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="SideBoundary" type="Area2D" parent="HBoxContainer/SideBoard" unique_id=1180680912]
|
||||
position = Vector2(155, 0)
|
||||
collision_layer = 8
|
||||
collision_mask = 0
|
||||
script = ExtResource("7_k5h0q")
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="HBoxContainer/SideBoard/SideBoundary" unique_id=1692634692]
|
||||
shape = SubResource("WorldBoundaryShape2D_htay1")
|
||||
|
||||
[node name="Timer" type="Timer" parent="." unique_id=1859393351]
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
class_name SideBoard extends ScrollContainer
|
||||
|
||||
@export var h_margin: = 8
|
||||
|
||||
# Panel is a bit easier to understand as a name, while Control is easier to use.
|
||||
var panel: Control
|
||||
var board: CardBoard
|
||||
|
||||
func _ready() -> void:
|
||||
panel = $Panel
|
||||
panel.custom_minimum_size = Vector2(size.x, 0)
|
||||
board = owner
|
||||
|
||||
func _start_drag(draggable: Draggable) -> void:
|
||||
assert(false)
|
||||
|
||||
func get_nearest_legal_position(from: Vector2, target: Draggable) -> Vector2:
|
||||
var children := get_children_sorted()
|
||||
|
||||
var nearest_child_below: Draggable
|
||||
var nearest_child_above: Draggable
|
||||
var min_distance_above: float
|
||||
var min_distance_below: float
|
||||
|
||||
|
||||
for i in range(children.size()):
|
||||
if children[i].home.y < from.y:
|
||||
nearest_child_above = children[i]
|
||||
min_distance_above = children[i].get_size().y/2 + h_margin + target.get_size().y/2
|
||||
if i > 0:
|
||||
nearest_child_below = children[i-1]
|
||||
min_distance_below = children[i-1].get_size().y/2 + h_margin + target.get_size().y/2
|
||||
|
||||
if nearest_child_above:
|
||||
from.y = minf(from.y, nearest_child_above.home.y - min_distance_above)
|
||||
if nearest_child_below:
|
||||
from.y = maxf(from.y, nearest_child_below.home.y - min_distance_below)
|
||||
|
||||
from.x = _get_x_pos(target)
|
||||
|
||||
return from
|
||||
|
||||
func can_accept_drop(draggable: Draggable) -> bool:
|
||||
return (draggable is Card and board.is_memory_board) or draggable is StickyNote
|
||||
|
||||
# FIXME: this function seems to not be used.
|
||||
func handle_drop(draggable: Draggable) -> int:
|
||||
if not can_accept_drop(draggable):
|
||||
return Draggable.DropResult.REJECTED
|
||||
return Draggable.DropResult.ACCEPTED
|
||||
|
||||
# FIXME: integrate this into functions mentioned above!
|
||||
func accept_drop(draggable: Draggable) -> bool:
|
||||
if can_accept_drop(draggable):
|
||||
draggable.home = get_nearest_legal_position(draggable.global_position, draggable)
|
||||
draggable.reparent(panel)
|
||||
_scoot_children()
|
||||
return true
|
||||
return false
|
||||
|
||||
func populate(with_notes: Array) -> Array[StickyNote]:
|
||||
var notes: Array[StickyNote] = []
|
||||
|
||||
for note: StickyNote in with_notes:
|
||||
notes.append(note)
|
||||
panel.add_child(note)
|
||||
|
||||
_scoot_children(true)
|
||||
|
||||
return notes
|
||||
|
||||
|
||||
func _scoot_children(force_packing: bool = false):
|
||||
#FIXME: the non-force-packing algorythm is borked right now
|
||||
force_packing = true
|
||||
var children := get_children_sorted()
|
||||
|
||||
if children.size() < 2: return
|
||||
|
||||
var min_size := h_margin
|
||||
|
||||
for child in children:
|
||||
min_size += child.get_size().y + h_margin
|
||||
|
||||
# If space is too tight, children will be spaced optimally.
|
||||
if maxf(min_size, children[-1].home.y + children[-1].get_size().y) > (size.y - h_margin) or force_packing:
|
||||
var top :float = h_margin
|
||||
|
||||
for child in children:
|
||||
child.home = Vector2(_get_x_pos(child), top + child.get_size().y/2)
|
||||
top += h_margin + child.get_size().y
|
||||
child.animate_home()
|
||||
|
||||
else:
|
||||
children[1].home.y = maxf(children[0].get_size().y + h_margin, children[1].home.y)
|
||||
var last_distance := 0.0
|
||||
for i in range(1, children.size()-1):
|
||||
var min_distance_bottom: float = children[i+1].get_size().y/2 + h_margin + children[i].get_size().y/2
|
||||
|
||||
var bottom_distance: float = children[i+1].home.y - children[i].home.y
|
||||
|
||||
if bottom_distance < min_distance_bottom:
|
||||
children[i-1].home.y = children[i-1].home.y - min(last_distance, bottom_distance)
|
||||
|
||||
children[i].home.y = children[i-1].home.y + min_distance_bottom
|
||||
|
||||
last_distance = maxf(children[i].home.y - children[i-1].home.y - min_distance_bottom, 0)
|
||||
|
||||
for child in children:
|
||||
child.home.x = _get_x_pos(child)
|
||||
child.animate_home()
|
||||
|
||||
if children[-1]:
|
||||
panel.custom_minimum_size.y = children[-1].home.y + children[-1].get_size().y
|
||||
|
||||
func get_children_sorted() -> Array[Draggable]:
|
||||
var children: Array[Draggable] = []
|
||||
for child in panel.get_children(true):
|
||||
if child is Draggable:
|
||||
children.append(child)
|
||||
|
||||
children.sort_custom(_by_position)
|
||||
|
||||
return children
|
||||
|
||||
func _get_x_pos(target: Draggable) -> float:
|
||||
if target is StickyNote:
|
||||
return size.x - 256
|
||||
if target is Card:
|
||||
return size.x - 160
|
||||
|
||||
return 42
|
||||
|
||||
|
||||
func _by_position(a: Draggable, b: Draggable) -> bool:
|
||||
return a.position.y < b.position.y
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cwc0k3mtj4k1f
|
||||
|
|
@ -0,0 +1 @@
|
|||
class_name SideBoundary extends Area2D
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://c7gbr6rdk843f
|
||||
|
|
@ -71,8 +71,7 @@ func init(sticky_name: String = "sticky_note", card_id: StringName = "-1") -> vo
|
|||
parent_id = StringName(card_id.rsplit(".", false, 1)[0])
|
||||
sticky_id = card_id
|
||||
|
||||
# first digit of the card id is 0-3 for youth cards.
|
||||
from_youth = (card_id as String)[0] as int < 4
|
||||
from_youth = int((card_id as String)[0]) < (Scenes.id.TRANSITION as int)
|
||||
|
||||
func _ready() -> void:
|
||||
super._ready()
|
||||
|
|
@ -112,14 +111,19 @@ func end_drag() -> Node:
|
|||
|
||||
## 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
|
||||
var closest : Node = 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
|
||||
|
||||
if area is SideBoundary:
|
||||
if not closest:
|
||||
closest = area
|
||||
|
||||
return closest
|
||||
|
||||
|
|
@ -127,3 +131,7 @@ func _find_drop_target() -> Node:
|
|||
## 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()
|
||||
|
||||
|
||||
func get_size() -> Vector2:
|
||||
return Vector2($CollisionShape2D.shape.height, $CollisionShape2D.shape.radius*2)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ height = 312.0
|
|||
|
||||
[node name="sticky-note" type="Area2D" unique_id=1136333559]
|
||||
collision_layer = 2
|
||||
collision_mask = 6
|
||||
collision_mask = 14
|
||||
priority = 100
|
||||
script = ExtResource("1_yvh5n")
|
||||
text = "card"
|
||||
|
|
@ -37,12 +37,13 @@ anchor_left = 0.5
|
|||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -52.0
|
||||
offset_top = -50.0
|
||||
offset_left = -43.0
|
||||
offset_top = -46.0
|
||||
offset_right = 243.0
|
||||
offset_bottom = 47.0
|
||||
offset_bottom = 42.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_default_cursor_shape = 13
|
||||
theme = ExtResource("3_qmm0h")
|
||||
theme_type_variation = &"card_text"
|
||||
text = "sticksum ipsum dolor sit amet met post-it sulcum dulce est 3M, et tesa est."
|
||||
|
|
|
|||
Loading…
Reference in New Issue