Compare commits
No commits in common. "5b034a9ce7e82da512b9f8850861849e785d7956" and "0b1c726c2edc99d028713b5400ddfc067060c5cb" have entirely different histories.
5b034a9ce7
...
0b1c726c2e
|
|
@ -7,6 +7,9 @@ extends RoomTemplate
|
||||||
@onready var card_picker: CardPicker = %Picker
|
@onready var card_picker: CardPicker = %Picker
|
||||||
@onready var ui: Control = %UI
|
@onready var ui: Control = %UI
|
||||||
|
|
||||||
|
# Is populated by child cardboard instead of onready.
|
||||||
|
var card_board: CardBoard
|
||||||
|
|
||||||
func start_room():
|
func start_room():
|
||||||
%UI.show()
|
%UI.show()
|
||||||
$logic/PlayerController.process_mode = Node.PROCESS_MODE_INHERIT
|
$logic/PlayerController.process_mode = Node.PROCESS_MODE_INHERIT
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
## Localization Utility class to move lists of keys out of difficult to read code
|
|
||||||
extends Node
|
|
||||||
|
|
||||||
func get_memento_prompt(count: int) -> StringName:
|
|
||||||
return TranslationServer.translate(_memento_prompts.get(count, ""))
|
|
||||||
|
|
||||||
func get_story_caption(id: Scenes.id) -> StringName:
|
|
||||||
return TranslationServer.translate(_story_captions.get(id, ""))
|
|
||||||
|
|
||||||
|
|
||||||
const _memento_prompts: Dictionary[int, StringName] = {
|
|
||||||
1: "There are three Mementos left to find.",
|
|
||||||
2: "You have collected half of the mementos.",
|
|
||||||
3: "Find the last Memento to complete the Board.",
|
|
||||||
4: "Combine cards to order your thoughts.",
|
|
||||||
}
|
|
||||||
|
|
||||||
const _story_captions : Dictionary[Scenes.id, StringName] = {
|
|
||||||
Scenes.id.YOUTH_DRAVEN: "Starlight",
|
|
||||||
Scenes.id.YOUTH_CHILDHOOD: "crafted Mask",
|
|
||||||
Scenes.id.YOUTH_VOICE_TRAINING: "Comic Stash",
|
|
||||||
Scenes.id.YOUTH_JUI_JUTSU: "Sports Clothes",
|
|
||||||
Scenes.id.TRANSITION: "Move on",
|
|
||||||
Scenes.id.ADULT_DND: "colorful Dice",
|
|
||||||
Scenes.id.ADULT_VOLUNTARY: "Gemstone Art",
|
|
||||||
Scenes.id.ADULT_CHRISTMAS: "Chat Messages",
|
|
||||||
Scenes.id.ADULT_EATING: "Dishes",
|
|
||||||
Scenes.id.ADULT_UNI: "Science Poster",
|
|
||||||
Scenes.id.ADULT_THERAPY: "Doctors Note",
|
|
||||||
Scenes.id.ADULT_BURNOUT: "Paperwork",
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://26fa8xwylhxl
|
|
||||||
|
|
@ -4,7 +4,6 @@ var initialised: bool = false
|
||||||
var id: State.rooms = State.rooms.NULL
|
var id: State.rooms = State.rooms.NULL
|
||||||
|
|
||||||
@onready var scene_player : AnimationPlayer = %ScenePlayer
|
@onready var scene_player : AnimationPlayer = %ScenePlayer
|
||||||
@onready var card_board : CardBoard # Optional Board, if present - set by the board in its own _ready()
|
|
||||||
|
|
||||||
var is_active: bool:
|
var is_active: bool:
|
||||||
set(value):
|
set(value):
|
||||||
|
|
@ -73,3 +72,4 @@ func unload():
|
||||||
## Override in subclasses to add custom scene preparation logic
|
## Override in subclasses to add custom scene preparation logic
|
||||||
func prepare_scene_start(_scene_id: Scenes.id, _is_repeating: bool) -> void:
|
func prepare_scene_start(_scene_id: Scenes.id, _is_repeating: bool) -> void:
|
||||||
await get_tree().process_frame # Dummy wait for LSP warning otherwise
|
await get_tree().process_frame # Dummy wait for LSP warning otherwise
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ class_name SaveGame extends Resource
|
||||||
# Board state - properly typed fields
|
# Board state - properly typed fields
|
||||||
@export var board_positions: Dictionary[StringName, Vector2] = {} # Position of all cards and stickies
|
@export var board_positions: Dictionary[StringName, Vector2] = {} # Position of all cards and stickies
|
||||||
@export var board_attachments: Dictionary[StringName, StringName] = {} # Sticky name → Card name (if attached)
|
@export var board_attachments: Dictionary[StringName, StringName] = {} # Sticky name → Card name (if attached)
|
||||||
|
@export var board_in_panel: Array[StringName] = [] # Stickies currently in the side panel
|
||||||
|
@export var board_randoms: Array[StringName] = [] # Items picked randomly
|
||||||
|
|
||||||
@export var is_childhood_board_complete: bool = false
|
@export var is_childhood_board_complete: bool = false
|
||||||
@export var player_position: Vector3 = Vector3.ZERO
|
@export var player_position: Vector3 = Vector3.ZERO
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -14,6 +14,7 @@ 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()
|
||||||
|
|
@ -37,22 +38,23 @@ var transfor_arr: Array[Transform2D] = [
|
||||||
@onready var label: Label = $Label
|
@onready var label: Label = $Label
|
||||||
@onready var background_sprite: AnimatedSprite2D = $AnimatedSprite2D
|
@onready var background_sprite: AnimatedSprite2D = $AnimatedSprite2D
|
||||||
|
|
||||||
|
@export var picked_random: bool = false
|
||||||
|
|
||||||
@export var wiggle_strength: float = 0.2
|
@export var wiggle_strength: float = 0.2
|
||||||
@export var wiggle_speed: float = 5
|
@export var wiggle_speed: float = 5
|
||||||
@export_range(1, 2) var scale_bump: float = 1.05
|
@export_range(1, 2) var scale_bump: float = 1.05
|
||||||
@export_range(1.0, 10.0) var bounce_speed: float = 5
|
@export_range(1.0, 10.0) var bounce_speed: float = 5
|
||||||
|
@export_range(1.0, 2.0) var highlight_brightness: float = 1.4
|
||||||
@export_color_no_alpha var highlight_color: Color = Color(1.4, 1.4, 1.4)
|
|
||||||
|
|
||||||
## Override set_highlight to add visual feedback for cards
|
## Override set_highlight to add visual feedback for cards
|
||||||
func set_highlight(value: bool) -> void:
|
func set_highlight(value: bool) -> void:
|
||||||
if value == _highlighted: return
|
if value != _highlighted:
|
||||||
_highlighted = value
|
_highlighted = value
|
||||||
|
|
||||||
|
if is_inside_tree() and is_node_ready():
|
||||||
if scale_tween: scale_tween.kill()
|
if scale_tween: scale_tween.kill()
|
||||||
if wiggle_tween: wiggle_tween.kill()
|
if wiggle_tween: wiggle_tween.kill()
|
||||||
if brightness_tween: brightness_tween.kill()
|
if brightness_tween: brightness_tween.kill()
|
||||||
|
|
||||||
if _highlighted:
|
if _highlighted:
|
||||||
scale_tween = get_tree().create_tween()
|
scale_tween = get_tree().create_tween()
|
||||||
scale_tween.tween_property(self, "scale", Vector2(scale_bump, scale_bump), 0.1)
|
scale_tween.tween_property(self, "scale", Vector2(scale_bump, scale_bump), 0.1)
|
||||||
|
|
@ -60,8 +62,8 @@ func set_highlight(value: bool) -> void:
|
||||||
wiggle_tween.tween_property(self, "wiggle_intensity", 1, 0.2)
|
wiggle_tween.tween_property(self, "wiggle_intensity", 1, 0.2)
|
||||||
brightness_tween = get_tree().create_tween()
|
brightness_tween = get_tree().create_tween()
|
||||||
brightness_tween.set_parallel(true)
|
brightness_tween.set_parallel(true)
|
||||||
brightness_tween.tween_property(background_sprite, "modulate", highlight_color, 0.15)
|
brightness_tween.tween_property(background_sprite, "modulate", Color(highlight_brightness, highlight_brightness, highlight_brightness), 0.15)
|
||||||
brightness_tween.tween_property(label, "modulate", highlight_color, 0.15)
|
brightness_tween.tween_property(label, "modulate", Color(highlight_brightness, highlight_brightness, highlight_brightness), 0.15)
|
||||||
else:
|
else:
|
||||||
scale_tween = get_tree().create_tween()
|
scale_tween = get_tree().create_tween()
|
||||||
scale_tween.tween_property(self, "scale", Vector2(1, 1), 0.3)
|
scale_tween.tween_property(self, "scale", Vector2(1, 1), 0.3)
|
||||||
|
|
@ -71,7 +73,21 @@ func set_highlight(value: bool) -> void:
|
||||||
brightness_tween.set_parallel(true)
|
brightness_tween.set_parallel(true)
|
||||||
brightness_tween.tween_property(background_sprite, "modulate", Color.WHITE, 0.2)
|
brightness_tween.tween_property(background_sprite, "modulate", Color.WHITE, 0.2)
|
||||||
brightness_tween.tween_property(label, "modulate", Color.WHITE, 0.2)
|
brightness_tween.tween_property(label, "modulate", Color.WHITE, 0.2)
|
||||||
|
else:
|
||||||
|
if _highlighted:
|
||||||
|
scale = Vector2(scale_bump, scale_bump)
|
||||||
|
wiggle_intensity = 1
|
||||||
|
if background_sprite:
|
||||||
|
background_sprite.modulate = Color(highlight_brightness, highlight_brightness, highlight_brightness)
|
||||||
|
if label:
|
||||||
|
label.modulate = Color(highlight_brightness, highlight_brightness, highlight_brightness)
|
||||||
|
else:
|
||||||
|
scale = Vector2(1,1)
|
||||||
|
wiggle_intensity = 0
|
||||||
|
if background_sprite:
|
||||||
|
background_sprite.modulate = Color.WHITE
|
||||||
|
if label:
|
||||||
|
label.modulate = Color.WHITE
|
||||||
|
|
||||||
@export var voice_line: AudioStream = null
|
@export var voice_line: AudioStream = null
|
||||||
@export var is_dragable: bool = false
|
@export var is_dragable: bool = false
|
||||||
|
|
@ -137,13 +153,14 @@ func init(card_name: String = "card", own_id:StringName = "-1") -> void:
|
||||||
if card_name != "c_void":
|
if card_name != "c_void":
|
||||||
text = card_name
|
text = card_name
|
||||||
if !card_name.begins_with("c"):
|
if !card_name.begins_with("c"):
|
||||||
push_error("Illegal card!", card_name, own_id)
|
push_error("Illegal card.")
|
||||||
card_id = own_id
|
card_id = own_id
|
||||||
name = card_name
|
name = card_name
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
super._ready()
|
input_event.connect(_on_input_event)
|
||||||
|
|
||||||
_handle_wiggle(0)
|
_handle_wiggle(0)
|
||||||
_on_text_updated.call_deferred()
|
_on_text_updated.call_deferred()
|
||||||
|
|
||||||
|
|
@ -186,17 +203,35 @@ func _process(delta: float) -> void:
|
||||||
|
|
||||||
func _handle_wiggle(delta):
|
func _handle_wiggle(delta):
|
||||||
wiggle_pos += delta * wiggle_speed * wiggle_intensity
|
wiggle_pos += delta * wiggle_speed * wiggle_intensity
|
||||||
|
|
||||||
rotation = noise.get_noise_1d(wiggle_pos)*wiggle_strength
|
rotation = noise.get_noise_1d(wiggle_pos)*wiggle_strength
|
||||||
|
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if event is InputEventMouseButton:
|
||||||
|
if event.button_index == MOUSE_BUTTON_LEFT and not event.pressed:
|
||||||
|
is_dragged = false
|
||||||
|
|
||||||
func _on_mouse_entered() -> void:
|
func _on_mouse_entered() -> void:
|
||||||
super._on_mouse_entered()
|
if not Input.is_action_pressed("mouse_left"):
|
||||||
|
# Do nothing if mouse hovers over sticky_note (it has higher priority)
|
||||||
|
if has_sticky_note_attached():
|
||||||
|
if current_sticky_note and current_sticky_note.highlighted:
|
||||||
|
return
|
||||||
|
if "handle_hover" in owner:
|
||||||
|
owner.handle_hover(self)
|
||||||
|
|
||||||
func _on_mouse_exited():
|
func _on_mouse_exited():
|
||||||
super._on_mouse_exited()
|
highlighted = false
|
||||||
if burn_state == burned.SINGED:
|
if burn_state == burned.SINGED:
|
||||||
burn_state = burned.NOT
|
burn_state = burned.NOT
|
||||||
|
|
||||||
|
func _on_input_event(_viewport, event, _shape_idx):
|
||||||
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||||||
|
if "handle_mouse_button" in owner and highlighted:
|
||||||
|
mouse_offset = get_viewport().get_mouse_position() - position
|
||||||
|
owner.handle_mouse_button(event, self)
|
||||||
|
|
||||||
func _move_card():
|
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())
|
||||||
|
|
@ -214,7 +249,8 @@ 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
|
||||||
# Keep sticky in current parent during preview (just move it visually)
|
sticky_note.reparent(self.get_parent())
|
||||||
|
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):
|
||||||
|
|
@ -228,7 +264,11 @@ 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")
|
||||||
|
|
@ -238,18 +278,29 @@ func attach_sticky_note(sticky_note: StickyNote) -> bool:
|
||||||
|
|
||||||
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()
|
||||||
if not former_child:
|
current_sticky_note = null
|
||||||
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:
|
||||||
if new_note == get_attached_sticky_note():
|
var tmp := remove_sticky_note()
|
||||||
return null
|
|
||||||
|
|
||||||
var old_note := remove_sticky_note()
|
|
||||||
attach_sticky_note(new_note)
|
attach_sticky_note(new_note)
|
||||||
return old_note
|
return tmp
|
||||||
|
|
||||||
|
# This makes sure this node highlights itself when focus has left the sticky note.
|
||||||
|
func check_hover():
|
||||||
|
# Re-trigger hover handling - owner will decide if this should be highlighted
|
||||||
|
_on_mouse_entered()
|
||||||
|
|
||||||
|
func reclaim_sticky_note():
|
||||||
|
current_sticky_note.on_board = false
|
||||||
|
current_sticky_note.tween_transform_to(Transform2D(0, to_global(sticky_note_position)))
|
||||||
|
await current_sticky_note.transform_tween_finished
|
||||||
|
current_sticky_note.reparent(self)
|
||||||
|
current_sticky_note.owner = self.owner
|
||||||
|
|
||||||
|
|
||||||
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
||||||
|
|
@ -286,7 +337,7 @@ func handle_drop(draggable: StickyNote) -> int:
|
||||||
## Retrieves the sticky that was exchanged during last drop
|
## Retrieves the sticky that was exchanged during last drop
|
||||||
## Clears the reference after retrieval
|
## Clears the reference after retrieval
|
||||||
func get_last_exchanged_sticky() -> StickyNote:
|
func get_last_exchanged_sticky() -> StickyNote:
|
||||||
var result := _last_exchanged_sticky
|
var result = _last_exchanged_sticky
|
||||||
_last_exchanged_sticky = null
|
_last_exchanged_sticky = null
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
@ -295,4 +346,4 @@ 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 _get_board()
|
return owner if owner is CardBoard else get_parent()
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,10 @@
|
||||||
size = Vector2(277, 231)
|
size = Vector2(277, 231)
|
||||||
|
|
||||||
[node name="Card" type="Area2D"]
|
[node name="Card" type="Area2D"]
|
||||||
collision_layer = 4
|
|
||||||
collision_mask = 0
|
|
||||||
priority = 50
|
priority = 50
|
||||||
script = ExtResource("1_emip0")
|
script = ExtResource("1_emip0")
|
||||||
text = "asdf"
|
text = "asdf"
|
||||||
|
metadata/_custom_type_script = "uid://ddy8kb2hjvgss"
|
||||||
metadata/type = "card"
|
metadata/type = "card"
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||||
|
|
@ -39,3 +38,6 @@ text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
autowrap_mode = 3
|
autowrap_mode = 3
|
||||||
|
|
||||||
|
[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"]
|
||||||
|
[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"]
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,6 @@ extends Area2D
|
||||||
## Base class for draggable UI elements (Cards and StickyNotes)
|
## Base class for draggable UI elements (Cards and StickyNotes)
|
||||||
## Provides common dragging behavior and boundary protection
|
## Provides common dragging behavior and boundary protection
|
||||||
|
|
||||||
## Margin from screen edges when confining to screen bounds
|
|
||||||
@export var screen_margin: float = 50.0
|
|
||||||
|
|
||||||
## Drop result codes for DropTarget pattern
|
## Drop result codes for DropTarget pattern
|
||||||
enum DropResult {
|
enum DropResult {
|
||||||
ACCEPTED, # Drop successful, item is now owned by target
|
ACCEPTED, # Drop successful, item is now owned by target
|
||||||
|
|
@ -14,7 +11,10 @@ enum DropResult {
|
||||||
EXCHANGED # Swap occurred, exchanged item needs handling
|
EXCHANGED # Swap occurred, exchanged item needs handling
|
||||||
}
|
}
|
||||||
|
|
||||||
var mouse_over: bool = false
|
## Static helper to check if a node implements DropTarget pattern
|
||||||
|
## DropTarget pattern requires: can_accept_drop(draggable) and handle_drop(draggable)
|
||||||
|
static func is_drop_target(node: Node) -> bool:
|
||||||
|
return node != null and node.has_method("can_accept_drop") and node.has_method("handle_drop")
|
||||||
|
|
||||||
var is_dragged: bool = false:
|
var is_dragged: bool = false:
|
||||||
set(dragged):
|
set(dragged):
|
||||||
|
|
@ -34,72 +34,17 @@ var highlighted: bool:
|
||||||
func set_highlight(value: bool) -> void:
|
func set_highlight(value: bool) -> void:
|
||||||
_highlighted = value
|
_highlighted = value
|
||||||
|
|
||||||
|
## Margin from screen edges when confining to screen bounds
|
||||||
|
@export var screen_margin: float = 50.0
|
||||||
|
|
||||||
## Drag state tracking
|
## Drag state tracking
|
||||||
var _drag_start_position: Vector2
|
var _drag_start_position: Vector2
|
||||||
var _mouse_drag_offset: Vector2
|
var _mouse_drag_offset: Vector2
|
||||||
var _drag_source: Node = null # Where the drag started from
|
var _drag_source: Node = null # Where the drag started from
|
||||||
|
|
||||||
## === SETUP ###
|
|
||||||
func _ready() -> void:
|
|
||||||
mouse_entered.connect(_on_mouse_entered)
|
|
||||||
mouse_exited.connect(_on_mouse_exited)
|
|
||||||
input_event.connect(_on_input_event)
|
|
||||||
|
|
||||||
|
|
||||||
func _get_hover_handler() -> Node:
|
|
||||||
var parent := get_parent()
|
|
||||||
while parent and not parent.has_method("handle_hover"):
|
|
||||||
parent = parent.get_parent()
|
|
||||||
return parent
|
|
||||||
|
|
||||||
|
|
||||||
## Walks up the scene tree to find the CardBoard
|
|
||||||
func _get_board() -> Node:
|
|
||||||
var node := get_parent()
|
|
||||||
while node:
|
|
||||||
if node.has_method("handle_mouse_button"):
|
|
||||||
return node
|
|
||||||
node = node.get_parent()
|
|
||||||
return null
|
|
||||||
|
|
||||||
## === DRAG LIFECYCLE METHODS ===
|
## === DRAG LIFECYCLE METHODS ===
|
||||||
## Override these in Card and StickyNote for specific behavior
|
## Override these in Card and StickyNote for specific behavior
|
||||||
|
|
||||||
func _on_mouse_entered() -> void:
|
|
||||||
prints("Draggable[base]._on_mouse_entered", self, self.name)
|
|
||||||
mouse_over = true
|
|
||||||
var handler := _get_hover_handler()
|
|
||||||
if handler: handler.handle_hover(self)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_mouse_exited() -> void:
|
|
||||||
prints("Draggable[base]._on_mouse_exited", self, self.name)
|
|
||||||
mouse_over = false
|
|
||||||
var handler := _get_hover_handler()
|
|
||||||
if handler: handler.handle_hover(self)
|
|
||||||
|
|
||||||
|
|
||||||
## Handles global input events (used to catch mouse release during drag)
|
|
||||||
func _input(event: InputEvent) -> void:
|
|
||||||
if event is InputEventMouseButton:
|
|
||||||
if event.button_index == MOUSE_BUTTON_LEFT and not event.pressed:
|
|
||||||
if is_dragged:
|
|
||||||
is_dragged = false
|
|
||||||
# Trigger the drop logic
|
|
||||||
var board := _get_board()
|
|
||||||
if board and board.has_method("_end_drag"):
|
|
||||||
board._end_drag(self)
|
|
||||||
|
|
||||||
|
|
||||||
## Handles input events on this Area2D (used to start drag)
|
|
||||||
func _on_input_event(_viewport, event, _shape_idx):
|
|
||||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
|
||||||
if highlighted:
|
|
||||||
var board := _get_board()
|
|
||||||
if board and board.has_method("handle_mouse_button"):
|
|
||||||
board.handle_mouse_button(event, self)
|
|
||||||
|
|
||||||
|
|
||||||
## Starts a drag operation
|
## Starts a drag operation
|
||||||
func start_drag(mouse_offset: Vector2) -> void:
|
func start_drag(mouse_offset: Vector2) -> void:
|
||||||
_drag_start_position = global_position
|
_drag_start_position = global_position
|
||||||
|
|
@ -120,24 +65,31 @@ func find_drop_target() -> Node:
|
||||||
return get_parent()
|
return get_parent()
|
||||||
|
|
||||||
## Called after drop to clean up drag state
|
## Called after drop to clean up drag state
|
||||||
func end_drag() -> Node:
|
func end_drag() -> void:
|
||||||
is_dragged = false
|
is_dragged = false
|
||||||
_drag_source = null
|
_drag_source = null
|
||||||
return null
|
|
||||||
|
|
||||||
|
|
||||||
## Confines this draggable element to stay within screen or container bounds
|
## Confines this draggable element to stay within screen or container bounds
|
||||||
## Skip this check if a sticky note is attached to a card
|
## Skip this check if a sticky note is attached to a card
|
||||||
func confine_to_screen() -> void:
|
func confine_to_screen() -> void:
|
||||||
|
# Skip if this is a sticky note attached to a card
|
||||||
|
if self is StickyNote:
|
||||||
|
var sticky := self as StickyNote
|
||||||
|
if sticky.attached_to is Card:
|
||||||
|
return
|
||||||
|
|
||||||
# Try to get bounds from parent container
|
# Try to get bounds from parent container
|
||||||
var bounds := _get_container_bounds()
|
var bounds := _get_container_bounds()
|
||||||
|
|
||||||
|
# If no container bounds, use viewport/screen bounds
|
||||||
|
if bounds == Rect2():
|
||||||
|
bounds = _get_viewport_bounds()
|
||||||
|
|
||||||
# If we have valid bounds, clamp position
|
# If we have valid bounds, clamp position
|
||||||
if bounds != Rect2():
|
if bounds != Rect2():
|
||||||
position.x = clampf(position.x, bounds.position.x, bounds.position.x + bounds.size.x)
|
position.x = clampf(position.x, bounds.position.x, bounds.position.x + bounds.size.x)
|
||||||
position.y = clampf(position.y, bounds.position.y, bounds.position.y + bounds.size.y)
|
position.y = clampf(position.y, bounds.position.y, bounds.position.y + bounds.size.y)
|
||||||
|
|
||||||
|
|
||||||
## Gets the bounds of the parent container if it exists and is a Control node
|
## Gets the bounds of the parent container if it exists and is a Control node
|
||||||
func _get_container_bounds() -> Rect2:
|
func _get_container_bounds() -> Rect2:
|
||||||
var parent := get_parent()
|
var parent := get_parent()
|
||||||
|
|
@ -153,11 +105,25 @@ func _get_container_bounds() -> Rect2:
|
||||||
control.size.y - screen_margin * 2
|
control.size.y - screen_margin * 2
|
||||||
)
|
)
|
||||||
|
|
||||||
# Default: whole screen
|
# Check if parent is a Node2D with defined boundaries
|
||||||
var viewport_size := get_viewport().get_visible_rect().size
|
# (for future support of non-Control containers)
|
||||||
|
if parent is Node2D:
|
||||||
|
# For now, return empty rect - could be extended in the future
|
||||||
|
# to check for custom boundary properties
|
||||||
|
pass
|
||||||
|
|
||||||
|
return Rect2()
|
||||||
|
|
||||||
|
## Gets the viewport bounds as fallback
|
||||||
|
func _get_viewport_bounds() -> Rect2:
|
||||||
|
var viewport := get_viewport()
|
||||||
|
if viewport:
|
||||||
|
var viewport_size := viewport.get_visible_rect().size
|
||||||
return Rect2(
|
return Rect2(
|
||||||
screen_margin,
|
screen_margin,
|
||||||
screen_margin,
|
screen_margin,
|
||||||
viewport_size.x - screen_margin * 2,
|
viewport_size.x - screen_margin * 2,
|
||||||
viewport_size.y - screen_margin * 2
|
viewport_size.y - screen_margin * 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return Rect2()
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ _data = {
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="board" type="PanelContainer"]
|
[node name="board" type="PanelContainer"]
|
||||||
|
z_index = -100
|
||||||
material = SubResource("ShaderMaterial_ttqei")
|
material = SubResource("ShaderMaterial_ttqei")
|
||||||
clip_contents = true
|
clip_contents = true
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
|
|
@ -163,18 +164,20 @@ script = ExtResource("3_8v4c4")
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="CardZone" type="Control" parent="HBoxContainer"]
|
[node name="dropzone" type="Panel" parent="HBoxContainer"]
|
||||||
unique_name_in_owner = true
|
|
||||||
self_modulate = Color(1, 1, 1, 0)
|
self_modulate = Color(1, 1, 1, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
mouse_filter = 1
|
mouse_filter = 1
|
||||||
|
|
||||||
[node name="NoteZone" type="Control" parent="HBoxContainer"]
|
[node name="ScrollContainer" type="ScrollContainer" parent="HBoxContainer"]
|
||||||
unique_name_in_owner = true
|
clip_contents = false
|
||||||
custom_minimum_size = Vector2(400, 0)
|
layout_mode = 2
|
||||||
|
horizontal_scroll_mode = 0
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/ScrollContainer"]
|
||||||
|
z_index = 120
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
mouse_filter = 1
|
|
||||||
|
|
||||||
[node name="instructions_panel" type="PanelContainer" parent="."]
|
[node name="instructions_panel" type="PanelContainer" parent="."]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,14 @@ 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: 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
|
|
||||||
|
|
||||||
|
|
||||||
signal transform_tween_finished
|
signal transform_tween_finished
|
||||||
|
|
||||||
@onready var background_sprite: AnimatedSprite2D = %BackgroundSprite
|
@onready var background_sprite: AnimatedSprite2D = %BackgroundSprite
|
||||||
|
|
@ -34,28 +27,34 @@ signal transform_tween_finished
|
||||||
var content: Node2D
|
var content: Node2D
|
||||||
var label: Label
|
var label: Label
|
||||||
|
|
||||||
|
@export var picked_random: bool = false
|
||||||
|
|
||||||
@export var shift_by: Vector2 = Vector2(-32, 0)
|
@export var shift_by: Vector2 = Vector2(-32, 0)
|
||||||
@export_color_no_alpha var highlight_color: Color = Color(1.5, 1.5, 1.5)
|
@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
|
## Override set_highlight to add visual feedback for sticky notes
|
||||||
func set_highlight(value: bool) -> void:
|
func set_highlight(value: bool) -> void:
|
||||||
if value != _highlighted:
|
if value != _highlighted:
|
||||||
_highlighted = value
|
_highlighted = value
|
||||||
|
|
||||||
|
if is_inside_tree() and is_node_ready():
|
||||||
if modulate_tween: modulate_tween.kill()
|
if modulate_tween: modulate_tween.kill()
|
||||||
if shift_tween: shift_tween.kill()
|
if shift_tween: shift_tween.kill()
|
||||||
|
|
||||||
if _highlighted:
|
if _highlighted:
|
||||||
modulate_tween = create_tween()
|
modulate_tween = get_tree().create_tween()
|
||||||
modulate_tween.tween_property(self, "modulate", highlight_color, 0.1)
|
modulate_tween.tween_property(self, "modulate", highlight_color, 0.1)
|
||||||
shift_tween = create_tween()
|
shift_tween = get_tree().create_tween()
|
||||||
shift_tween.tween_property(content, "position", shift_by, 0.2)
|
shift_tween.tween_property(content, "position", shift_by, 0.2)
|
||||||
else:
|
else:
|
||||||
modulate_tween = create_tween()
|
modulate_tween = get_tree().create_tween()
|
||||||
modulate_tween.tween_property(self, "modulate", Color(1, 1, 1), 0.3)
|
modulate_tween.tween_property(self, "modulate", Color(1, 1, 1), 0.3)
|
||||||
shift_tween = create_tween()
|
shift_tween = get_tree().create_tween()
|
||||||
shift_tween.tween_property(content, "position", Vector2.ZERO, 0.5)
|
shift_tween.tween_property(content, "position", Vector2.ZERO, 0.5)
|
||||||
|
else:
|
||||||
|
if _highlighted:
|
||||||
|
modulate = Color(1, 1, 1)
|
||||||
|
else:
|
||||||
|
modulate = Color(1, 1, 1)
|
||||||
|
|
||||||
@export var voice_line: AudioStream = null
|
@export var voice_line: AudioStream = null
|
||||||
@export var is_dragable: bool = false
|
@export var is_dragable: bool = false
|
||||||
|
|
@ -64,13 +63,7 @@ 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
|
||||||
|
|
@ -79,13 +72,19 @@ func init(sticky_name: String = "sticky_note", card_id: StringName = "-1") -> vo
|
||||||
sticky_id = card_id
|
sticky_id = card_id
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
super._ready()
|
|
||||||
label = $Content/Label
|
label = $Content/Label
|
||||||
background_sprite = $Content/BackgroundSprite
|
background_sprite = $Content/BackgroundSprite
|
||||||
content = $Content
|
content = $Content
|
||||||
|
|
||||||
_on_text_updated.call_deferred()
|
_on_text_updated.call_deferred()
|
||||||
|
|
||||||
|
input_event.connect(_on_input_event)
|
||||||
|
mouse_entered.connect(_on_mouse_entered)
|
||||||
|
mouse_exited.connect(_on_mouse_exited)
|
||||||
|
area_entered.connect(_on_area_enter)
|
||||||
|
area_exited.connect(_on_area_exit)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func _on_text_updated():
|
func _on_text_updated():
|
||||||
label.text = text
|
label.text = text
|
||||||
|
|
@ -93,30 +92,56 @@ func _on_text_updated():
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
_move_sticky_note(delta)
|
if get_overlapping_areas().size() > 0 and is_dragable and on_board:
|
||||||
|
for area in get_overlapping_areas():
|
||||||
|
if area is Card:
|
||||||
|
if not area.highlighted or self.highlighted:
|
||||||
|
var diff:Vector2 = position - area.position
|
||||||
|
position -= diff.normalized() * ((diff.length()-diameter)/diameter) * bounce_speed * (delta/(1.0/60))
|
||||||
|
|
||||||
|
_move_sticky_note()
|
||||||
|
|
||||||
## frame rate independent FIR smoothing filter
|
func _on_mouse_entered():
|
||||||
func _smooth(current: Vector2, goal: Vector2, delta: float) -> Vector2:
|
if not Input.is_action_pressed("mouse_left") and "handle_hover" in current_handle:
|
||||||
var k := pow(0.1, 60.0 * delta)
|
current_handle.handle_hover(self)
|
||||||
return (1.0-k) * current + k * goal
|
|
||||||
|
|
||||||
|
func _on_mouse_exited():
|
||||||
|
highlighted = false
|
||||||
|
# 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:
|
||||||
|
attached_to.check_hover()
|
||||||
|
|
||||||
func _move_sticky_note(delta: float) -> void:
|
func _on_area_enter(area: Area2D):
|
||||||
|
# Handle sticky note panel gap creation
|
||||||
|
if area is StickyNote and is_sticky_note_in_panel() and not is_dragged:
|
||||||
|
attached_to.create_gap()
|
||||||
|
|
||||||
|
func _on_area_exit(area: Area2D):
|
||||||
|
# Handle sticky note panel gap collapse
|
||||||
|
if area is StickyNote and is_sticky_note_in_panel():
|
||||||
|
attached_to.collapse_gap()
|
||||||
|
|
||||||
|
func _on_input_event(_viewport, event, _shape_idx):
|
||||||
|
if event is InputEventMouseButton and "handle_mouse_button" in current_handle:
|
||||||
|
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
|
||||||
|
current_handle.handle_mouse_button(event, self)
|
||||||
|
|
||||||
|
func _move_sticky_note():
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
|
func is_sticky_note_attached() -> bool:
|
||||||
|
# FIXME: this breaks if attatched to is previousely freed because GODOT IS FUCKING STUPID
|
||||||
|
return attached_to is Card
|
||||||
|
|
||||||
|
func is_sticky_note_in_panel() -> bool:
|
||||||
|
## fixme ~> see above
|
||||||
|
return attached_to is StickyNotePanel
|
||||||
|
|
||||||
var transform_tween: Tween
|
var transform_tween: Tween
|
||||||
|
|
||||||
func tween_transform_to(target: Transform2D, duration: float = 0.25) ->void:
|
func tween_transform_to(target: Transform2D, duration: float = 0.25):
|
||||||
# Validate position to prevent teleporting
|
# Validate position to prevent teleporting
|
||||||
if not is_finite(target.origin.x) or not is_finite(target.origin.y):
|
if not is_finite(target.origin.x) or not is_finite(target.origin.y):
|
||||||
push_warning("StickyNote.tween_transform_to: Invalid position, skipping tween")
|
push_warning("StickyNote.tween_transform_to: Invalid position, skipping tween")
|
||||||
|
|
@ -135,23 +160,46 @@ func tween_transform_to(target: Transform2D, duration: float = 0.25) ->void:
|
||||||
|
|
||||||
# === DRAG LIFECYCLE OVERRIDES ===
|
# === DRAG LIFECYCLE OVERRIDES ===
|
||||||
|
|
||||||
func end_drag() -> Node:
|
## Track whether this sticky came from a panel (for exchange logic)
|
||||||
super.end_drag()
|
var _came_from_panel: bool = false
|
||||||
return _find_drop_target()
|
|
||||||
|
|
||||||
|
## Start drag: if in panel, immediately move to board
|
||||||
|
func start_drag(offset: Vector2) -> void:
|
||||||
|
super.start_drag(offset)
|
||||||
|
_came_from_panel = is_sticky_note_in_panel()
|
||||||
|
|
||||||
|
# If attached to a card, detach it first
|
||||||
|
if is_sticky_note_attached():
|
||||||
|
var card := attached_to as Card
|
||||||
|
if card and card.has_method("remove_sticky_note"):
|
||||||
|
card.remove_sticky_note()
|
||||||
|
|
||||||
|
# If in panel, immediately reparent to board for dragging
|
||||||
|
if _came_from_panel and current_handle:
|
||||||
|
var board := current_handle
|
||||||
|
var dropzone := board.get_node_or_null("HBoxContainer/dropzone")
|
||||||
|
if dropzone:
|
||||||
|
reparent(dropzone)
|
||||||
|
else:
|
||||||
|
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:
|
||||||
# Priority 1: Check for overlapping cards in dropzone
|
# Priority 1: Check for overlapping cards in dropzone
|
||||||
var closest : Card = null
|
|
||||||
for area in get_overlapping_areas():
|
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 and Draggable.is_drop_target(area):
|
||||||
|
return area
|
||||||
|
|
||||||
if area is Card:
|
# Priority 2: Check if dropped outside dropzone (over panel area)
|
||||||
if (not closest) or ((area.position - position).length() < (closest.position - position).length()):
|
if current_handle and not current_handle.is_in_dropzone(self):
|
||||||
closest = area
|
var target_panel := _find_nearest_panel()
|
||||||
|
if target_panel:
|
||||||
|
return target_panel
|
||||||
|
|
||||||
return closest
|
# Priority 3: Default to board (stay loose in dropzone)
|
||||||
|
return current_handle
|
||||||
|
|
||||||
## Find the nearest panel that can accept this sticky
|
## Find the nearest panel that can accept this sticky
|
||||||
func _find_nearest_panel() -> StickyNotePanel:
|
func _find_nearest_panel() -> StickyNotePanel:
|
||||||
|
|
@ -175,6 +223,3 @@ func _find_nearest_panel() -> StickyNotePanel:
|
||||||
# No empty panels found - will need to create one (handled by board)
|
# No empty panels found - will need to create one (handled by board)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
|
||||||
func confine_to_screen() -> void:
|
|
||||||
if attached_to is not Card: super.confine_to_screen()
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ radius = 48.0
|
||||||
height = 312.0
|
height = 312.0
|
||||||
|
|
||||||
[node name="sticky-note" type="Area2D"]
|
[node name="sticky-note" type="Area2D"]
|
||||||
|
z_index = 1
|
||||||
collision_layer = 2
|
collision_layer = 2
|
||||||
collision_mask = 6
|
|
||||||
priority = 100
|
priority = 100
|
||||||
script = ExtResource("1_yvh5n")
|
script = ExtResource("1_yvh5n")
|
||||||
text = "card"
|
text = "card"
|
||||||
|
|
|
||||||
|
|
@ -14,21 +14,15 @@ func _init(cstm_minimum_size: Vector2 = minimum_size, note_position: Vector2 = V
|
||||||
mouse_filter = MOUSE_FILTER_PASS
|
mouse_filter = MOUSE_FILTER_PASS
|
||||||
self_modulate = Color(1, 1, 1, 0)
|
self_modulate = Color(1, 1, 1, 0)
|
||||||
|
|
||||||
@onready var board : CardBoard = get_parent().get_parent() as CardBoard
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
custom_minimum_size = Vector2(custom_minimum_size.x, 0)
|
custom_minimum_size = Vector2(custom_minimum_size.x, 0)
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
var is_attatching: bool = false
|
||||||
var child := get_child(0) as StickyNote
|
func attatch_sticky_note(attatchment: StickyNote, custom_owner: Node, animate:bool = true):
|
||||||
if child and not child.is_dragged:
|
is_attatching = true
|
||||||
var k := pow(0.1, 60.0 * delta)
|
|
||||||
child.position = child.position * (1.0-k) + ancor_position * (k)
|
|
||||||
|
|
||||||
var is_attaching: bool = false
|
|
||||||
func attatch_sticky_note(attatchment: StickyNote, custom_handle: Node, animate:bool = true):
|
|
||||||
attached_sticky_note = attatchment
|
attached_sticky_note = attatchment
|
||||||
attatchment.current_handle = custom_handle
|
attatchment.current_handle = custom_owner
|
||||||
|
attatchment.owner = custom_owner
|
||||||
|
|
||||||
# Expand panel height
|
# Expand panel height
|
||||||
if animate:
|
if animate:
|
||||||
|
|
@ -40,10 +34,12 @@ func attatch_sticky_note(attatchment: StickyNote, custom_handle: Node, animate:b
|
||||||
# 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)
|
||||||
|
|
@ -59,10 +55,71 @@ func attatch_sticky_note(attatchment: StickyNote, custom_handle: Node, animate:b
|
||||||
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
|
||||||
|
|
||||||
func is_empty() -> bool:
|
is_attatching = false
|
||||||
return get_child_count() == 0
|
|
||||||
|
|
||||||
|
|
||||||
|
var is_gapped: bool = false
|
||||||
|
func create_gap():
|
||||||
|
var self_id := get_parent().get_children().find(self)
|
||||||
|
var next_id = min(self_id + 1, get_parent().get_child_count() - 1)
|
||||||
|
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:
|
||||||
|
is_gapped = true
|
||||||
|
var height_tween: Tween = create_tween()
|
||||||
|
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()
|
||||||
|
if not get_parent().get_children().find(self) == 0: get_parent().get_child(previous_id).collapse_gap()
|
||||||
|
|
||||||
|
func collapse_gap():
|
||||||
|
if is_gapped:
|
||||||
|
is_gapped = false
|
||||||
|
var height_tween: Tween = create_tween()
|
||||||
|
height_tween.tween_property(self, "custom_minimum_size", minimum_size, 0.1)
|
||||||
|
|
||||||
|
var invalid: bool = false
|
||||||
|
func clear_if_empty():
|
||||||
|
if !is_empty(): return
|
||||||
|
invalid = true
|
||||||
|
if attached_sticky_note.attached_to == self: attached_sticky_note.attached_to = null
|
||||||
|
var height_tween: Tween = create_tween()
|
||||||
|
height_tween.tween_property(self, "custom_minimum_size", Vector2.ZERO, 0.3)
|
||||||
|
await height_tween.finished
|
||||||
|
owner.on_sticky_panel_cleared(get_parent().get_children().find(self))
|
||||||
|
self.queue_free()
|
||||||
|
|
||||||
|
func replace_sticky_note_with(new_sticky_note: StickyNote):
|
||||||
|
if is_empty():
|
||||||
|
attached_sticky_note = new_sticky_note
|
||||||
|
|
||||||
|
func is_empty() -> bool:
|
||||||
|
return get_child_count() == 0 and not is_attatching
|
||||||
|
|
||||||
|
|
||||||
|
# === DROP TARGET PATTERN IMPLEMENTATION ===
|
||||||
|
|
||||||
|
## Checks if this panel can accept the given draggable
|
||||||
|
func can_accept_drop(draggable: Draggable) -> bool:
|
||||||
|
return draggable is StickyNote and is_empty()
|
||||||
|
|
||||||
|
## Handles dropping a sticky note onto this panel
|
||||||
|
func handle_drop(draggable: StickyNote) -> int:
|
||||||
|
if not can_accept_drop(draggable):
|
||||||
|
return Draggable.DropResult.REJECTED
|
||||||
|
|
||||||
|
# Attach sticky to this panel with animation
|
||||||
|
attatch_sticky_note(draggable, owner, true)
|
||||||
|
|
||||||
|
# Clean up other empty panels
|
||||||
|
for panel in get_parent().get_children():
|
||||||
|
if panel is StickyNotePanel and panel != self:
|
||||||
|
panel.clear_if_empty()
|
||||||
|
|
||||||
|
return Draggable.DropResult.ACCEPTED
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,14 @@ func _ready():
|
||||||
%SkipButton.pressed.connect(card_burned.emit)
|
%SkipButton.pressed.connect(card_burned.emit)
|
||||||
|
|
||||||
func burn_cards(_id: int, _repeat: bool = false) -> void:
|
func burn_cards(_id: int, _repeat: bool = false) -> void:
|
||||||
# Get all card names from the board (excluding stickies which start with "p")
|
var random_card_names: Array = State.save_game.board_randoms.duplicate()
|
||||||
var card_names: Array[StringName] = []
|
|
||||||
for item_name in State.save_game.board_positions.keys():
|
for card_name in random_card_names:
|
||||||
if not item_name.begins_with("p"):
|
if card_name.begins_with("p"):
|
||||||
card_names.append(item_name)
|
random_card_names.erase(card_name)
|
||||||
|
|
||||||
|
var random_cards: Array = HardCards.get_cards_by_name_array(random_card_names)["cards"]
|
||||||
|
|
||||||
# Get card instances and shuffle them
|
|
||||||
var random_cards: Array = HardCards.get_cards_by_name_array(card_names)["cards"]
|
|
||||||
random_cards.shuffle()
|
random_cards.shuffle()
|
||||||
|
|
||||||
for ancor:Control in ancors:
|
for ancor:Control in ancors:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
class_name CardPicker
|
class_name CardPicker extends CenterContainer
|
||||||
extends Playable
|
|
||||||
|
|
||||||
#fixme INI is probably redundant.
|
#fixme INI is probably redundant.
|
||||||
enum {
|
enum {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
class_name Interactable extends Node3D
|
class_name Interactable extends Node3D
|
||||||
|
|
||||||
@export var interaction: PackedScene = null
|
@export var interaction: PackedScene = null
|
||||||
var interaction_ui : Playable = null
|
var interaction_ui : Control = null
|
||||||
|
|
||||||
@onready var view: Node3D = $View
|
@onready var view: Node3D = $View
|
||||||
@onready var frame: Sprite3D = $Frame
|
@onready var frame: Sprite3D = $Frame
|
||||||
|
|
@ -65,7 +65,6 @@ func expand() -> void:
|
||||||
|
|
||||||
|
|
||||||
func collapse() -> void:
|
func collapse() -> void:
|
||||||
if not shown: return #TODO: test
|
|
||||||
shown = false
|
shown = false
|
||||||
if tween and tween.is_valid(): tween.kill()
|
if tween and tween.is_valid(): tween.kill()
|
||||||
tween = create_tween().set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_BACK)
|
tween = create_tween().set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_BACK)
|
||||||
|
|
@ -144,7 +143,7 @@ func interact() -> void:
|
||||||
shown = false
|
shown = false
|
||||||
await collapse()
|
await collapse()
|
||||||
|
|
||||||
# collapse other interactables BEFORE showing canvas
|
# Hide mouse and collapse other interactables BEFORE showing canvas
|
||||||
get_tree().call_group("interactables", "collapse")
|
get_tree().call_group("interactables", "collapse")
|
||||||
|
|
||||||
# Show the CanvasLayer so the story is visible full-screen
|
# Show the CanvasLayer so the story is visible full-screen
|
||||||
|
|
@ -165,7 +164,33 @@ func interact() -> void:
|
||||||
func _update_caption() -> void:
|
func _update_caption() -> void:
|
||||||
if interaction_ui is StoryPlayable:
|
if interaction_ui is StoryPlayable:
|
||||||
var story := interaction_ui as StoryPlayable
|
var story := interaction_ui as StoryPlayable
|
||||||
caption.text = I18n.get_story_caption(story.scene_id)
|
match story.scene_id:
|
||||||
|
Scenes.id.YOUTH_DRAVEN:
|
||||||
|
caption.text = TranslationServer.translate("Starlight")
|
||||||
|
Scenes.id.YOUTH_CHILDHOOD:
|
||||||
|
caption.text = TranslationServer.translate("crafted Mask")
|
||||||
|
Scenes.id.YOUTH_VOICE_TRAINING:
|
||||||
|
caption.text = TranslationServer.translate("Comic Stash")
|
||||||
|
Scenes.id.YOUTH_JUI_JUTSU:
|
||||||
|
caption.text = TranslationServer.translate("Sports Clothes")
|
||||||
|
Scenes.id.TRANSITION:
|
||||||
|
caption.text = TranslationServer.translate("Move on")
|
||||||
|
Scenes.id.ADULT_DND:
|
||||||
|
caption.text = TranslationServer.translate("colorful Dice")
|
||||||
|
Scenes.id.ADULT_VOLUNTARY:
|
||||||
|
caption.text = TranslationServer.translate("Gemstone Art")
|
||||||
|
Scenes.id.ADULT_CHRISTMAS:
|
||||||
|
caption.text = TranslationServer.translate("Chat Messages")
|
||||||
|
Scenes.id.ADULT_EATING:
|
||||||
|
caption.text = TranslationServer.translate("Dishes")
|
||||||
|
Scenes.id.ADULT_UNI:
|
||||||
|
caption.text = TranslationServer.translate("Science Poster")
|
||||||
|
Scenes.id.ADULT_THERAPY:
|
||||||
|
caption.text = TranslationServer.translate("Doctors Note")
|
||||||
|
Scenes.id.ADULT_BURNOUT:
|
||||||
|
caption.text = TranslationServer.translate("Paperwork")
|
||||||
|
_:
|
||||||
|
caption.text = ""
|
||||||
elif interaction_ui is CardBoard:
|
elif interaction_ui is CardBoard:
|
||||||
caption.text = TranslationServer.translate("Mind Board")
|
caption.text = TranslationServer.translate("Mind Board")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
extends CenterContainer
|
||||||
class_name StoryPlayable
|
class_name StoryPlayable
|
||||||
extends Playable
|
|
||||||
|
|
||||||
signal text_finished
|
signal text_finished
|
||||||
signal finished
|
signal finished
|
||||||
|
|
@ -148,7 +148,7 @@ func play():
|
||||||
show()
|
show()
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||||
|
|
||||||
# FIXME: Don't know how to do this.
|
# Don't know how to do this.
|
||||||
#%StoryScroll.grab_focus()
|
#%StoryScroll.grab_focus()
|
||||||
|
|
||||||
if name == "draven":
|
if name == "draven":
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
extends Control
|
|
||||||
class_name Playable
|
|
||||||
|
|
||||||
## Awaitable that encapsulates the core interaction with this Playable
|
|
||||||
func play() -> void:
|
|
||||||
await get_tree().process_frame # Dummy wait so this is a coroutine
|
|
||||||
|
|
||||||
func handle_hover(area: Draggable):
|
|
||||||
prints("Playable[base].handle_hover", area, area.name)
|
|
||||||
pass
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://dbmkkouhc0euw
|
|
||||||
|
|
@ -28,7 +28,6 @@ PromptManager="*res://addons/input_prompts/input_prompt_manager.gd"
|
||||||
Steam="*res://dev-util/steam.gd"
|
Steam="*res://dev-util/steam.gd"
|
||||||
Main="*res://singletons/main/main.tscn"
|
Main="*res://singletons/main/main.tscn"
|
||||||
HardCards="*res://dev-util/hardcoded_cards.tscn"
|
HardCards="*res://dev-util/hardcoded_cards.tscn"
|
||||||
I18n="*res://dev-util/i18n.gd"
|
|
||||||
|
|
||||||
[debug]
|
[debug]
|
||||||
|
|
||||||
|
|
@ -201,11 +200,7 @@ locale/test="de"
|
||||||
|
|
||||||
[layer_names]
|
[layer_names]
|
||||||
|
|
||||||
2d_physics/layer_1="World"
|
|
||||||
3d_physics/layer_1="Scene Geometry"
|
3d_physics/layer_1="Scene Geometry"
|
||||||
2d_physics/layer_2="Stickies"
|
|
||||||
2d_physics/layer_3="Cards"
|
|
||||||
2d_physics/layer_5="Interactable"
|
|
||||||
3d_physics/layer_5="UI_reveal"
|
3d_physics/layer_5="UI_reveal"
|
||||||
3d_physics/layer_6="UI_handle"
|
3d_physics/layer_6="UI_handle"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
[gd_scene load_steps=2 format=3 uid="uid://b752f680edsnv"]
|
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://bnskiyx1sksww" path="res://logic-scenes/board/physics-board.tscn" id="1_b12jd"]
|
|
||||||
|
|
||||||
[node name="BoardTests" type="Node"]
|
|
||||||
|
|
||||||
[node name="board" parent="." instance=ExtResource("1_b12jd")]
|
|
||||||
|
|
@ -55,7 +55,7 @@ func _load_games():
|
||||||
|
|
||||||
|
|
||||||
func _sort_saves() -> void:
|
func _sort_saves() -> void:
|
||||||
saves.sort_custom(func(a: SaveGame, b: SaveGame) -> bool:
|
saves.sort_custom(func(a: SaveGame, b: SaveGame) -> int:
|
||||||
return a.last_saved > b.last_saved
|
return a.last_saved > b.last_saved
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue