From 8b5a7b1989d7559699ce14ecdc54f7d72adc6698 Mon Sep 17 00:00:00 2001 From: Christopher Koch Date: Tue, 27 Jan 2026 19:09:55 -0500 Subject: [PATCH] hand show and hide --- .godot/editor/editor_layout.cfg | 6 +- .godot/editor/project_metadata.cfg | 2 +- .godot/editor/script_editor_cache.cfg | 30 ++-- scripts/Main.gd | 34 +++-- scripts/ui/HandDisplay.gd | 198 +++++++++++++++++++++++++- 5 files changed, 241 insertions(+), 29 deletions(-) diff --git a/.godot/editor/editor_layout.cfg b/.godot/editor/editor_layout.cfg index fe3a01b..6dda6e5 100644 --- a/.godot/editor/editor_layout.cfg +++ b/.godot/editor/editor_layout.cfg @@ -19,7 +19,7 @@ dock_filesystem_split=0 dock_filesystem_display_mode=0 dock_filesystem_file_sort=0 dock_filesystem_file_list_display_mode=1 -dock_filesystem_selected_paths=PackedStringArray("res://scripts/ui/MainMenu.gd") +dock_filesystem_selected_paths=PackedStringArray("res://scripts/ui/ActionLog.gd") dock_filesystem_uncollapsed_paths=PackedStringArray("Favorites", "res://", "res://scripts/", "res://scripts/visual/", "res://scripts/ui/", "res://scripts/game/", "res://scenes/") dock_3="Scene,Import" dock_4="FileSystem" @@ -36,8 +36,8 @@ selected_bottom_panel_item=0 [ScriptEditor] -open_scripts=["res://scripts/ui/ActionLog.gd", "res://scripts/autoload/CardDatabase.gd", "res://scripts/game/CardInstance.gd", "res://scripts/visual/CardVisual.gd", "res://scripts/game/CPPool.gd", "res://scripts/GameController.gd", "res://scripts/game/GameState.gd", "res://scripts/ui/GameUI.gd", "res://scripts/ui/HandDisplay.gd", "res://scripts/Main.gd", "res://scripts/ui/MainMenu.gd", "res://scripts/ui/PauseMenu.gd", "res://scripts/game/Player.gd", "res://scripts/visual/PlaymatRenderer.gd", "res://scripts/visual/TableCamera.gd", "res://scripts/visual/TableSetup.gd", "res://scripts/game/UndoSystem.gd"] -selected_script="res://scripts/ui/MainMenu.gd" +open_scripts=["res://scripts/ui/ActionLog.gd", "res://scripts/autoload/CardDatabase.gd", "res://scripts/game/CardInstance.gd", "res://scripts/visual/CardVisual.gd", "res://scripts/game/CPPool.gd", "res://scripts/ui/DamageDisplay.gd", "res://scripts/GameController.gd", "res://scripts/game/GameState.gd", "res://scripts/ui/GameUI.gd", "res://scripts/ui/HandDisplay.gd", "res://scripts/Main.gd", "res://scripts/ui/MainMenu.gd", "res://scripts/ui/PauseMenu.gd", "res://scripts/game/Player.gd", "res://scripts/visual/PlaymatRenderer.gd", "res://scripts/visual/TableCamera.gd", "res://scripts/visual/TableSetup.gd", "res://scripts/game/UndoSystem.gd"] +selected_script="res://scripts/game/CardInstance.gd" open_help=[] script_split_offset=140 list_split_offset=0 diff --git a/.godot/editor/project_metadata.cfg b/.godot/editor/project_metadata.cfg index 54e8c5a..9f8596a 100644 --- a/.godot/editor/project_metadata.cfg +++ b/.godot/editor/project_metadata.cfg @@ -10,7 +10,7 @@ run_reload_scripts=true [recent_files] scenes=["res://scenes/main.tscn"] -scripts=["res://scripts/visual/CardVisual.gd", "res://scripts/GameController.gd", "res://scripts/visual/PlaymatRenderer.gd", "res://scripts/ui/MainMenu.gd", "res://scripts/ui/GameUI.gd", "res://scripts/game/Player.gd", "res://scripts/game/CardInstance.gd", "res://scripts/game/CPPool.gd", "res://scripts/game/GameState.gd", "res://scripts/visual/TableCamera.gd"] +scripts=["res://scripts/ui/DamageDisplay.gd", "res://scripts/visual/CardVisual.gd", "res://scripts/GameController.gd", "res://scripts/visual/PlaymatRenderer.gd", "res://scripts/ui/MainMenu.gd", "res://scripts/ui/GameUI.gd", "res://scripts/game/Player.gd", "res://scripts/game/CardInstance.gd", "res://scripts/game/CPPool.gd", "res://scripts/game/GameState.gd"] [linked_properties] diff --git a/.godot/editor/script_editor_cache.cfg b/.godot/editor/script_editor_cache.cfg index 5dde3bf..f6e532b 100644 --- a/.godot/editor/script_editor_cache.cfg +++ b/.godot/editor/script_editor_cache.cfg @@ -3,11 +3,11 @@ state={ "bookmarks": PackedInt32Array(), "breakpoints": PackedInt32Array(), -"column": 18, +"column": 0, "folded_lines": Array[int]([]), "h_scroll_position": 0, "row": 305, -"scroll_position": 332.0, +"scroll_position": 331.0, "selection": false, "syntax_highlighter": "GDScript" } @@ -63,7 +63,7 @@ state={ "folded_lines": Array[int]([]), "h_scroll_position": 0, "row": 166, -"scroll_position": 161.0, +"scroll_position": 154.0, "selection": false, "syntax_highlighter": "GDScript" } @@ -91,7 +91,7 @@ state={ "folded_lines": Array[int]([]), "h_scroll_position": 0, "row": 225, -"scroll_position": 0.0, +"scroll_position": 226.0, "selection": false, "syntax_highlighter": "GDScript" } @@ -146,8 +146,8 @@ state={ "column": 0, "folded_lines": Array[int]([]), "h_scroll_position": 0, -"row": 0, -"scroll_position": 0.0, +"row": 33, +"scroll_position": 33.0, "selection": false, "syntax_highlighter": "GDScript" } @@ -185,10 +185,10 @@ state={ state={ "bookmarks": PackedInt32Array(), "breakpoints": PackedInt32Array(), -"column": 14, +"column": 26, "folded_lines": Array[int]([]), "h_scroll_position": 0, -"row": 29, +"row": 26, "scroll_position": 19.0, "selection": false, "syntax_highlighter": "GDScript" @@ -235,3 +235,17 @@ state={ "selection": false, "syntax_highlighter": "GDScript" } + +[res://scripts/ui/DamageDisplay.gd] + +state={ +"bookmarks": PackedInt32Array(), +"breakpoints": PackedInt32Array(), +"column": 0, +"folded_lines": Array[int]([]), +"h_scroll_position": 0, +"row": 0, +"scroll_position": 0.0, +"selection": false, +"syntax_highlighter": "GDScript" +} diff --git a/scripts/Main.gd b/scripts/Main.gd index 4b536d9..2377b9d 100644 --- a/scripts/Main.gd +++ b/scripts/Main.gd @@ -82,6 +82,7 @@ func _setup_ui() -> void: hand_display.card_hovered.connect(_on_hand_card_hovered) hand_display.card_unhovered.connect(_on_hand_card_unhovered) hand_display.card_selected.connect(_on_hand_card_selected) + hand_display.hand_minimized_changed.connect(_on_hand_minimized_changed) # Create damage displays (positioned by 3D camera overlay later) for i in range(2): @@ -89,18 +90,25 @@ func _setup_ui() -> void: damage_displays.append(damage_display) func _position_hand_display() -> void: - # Get viewport size and position hand at bottom var viewport = get_viewport() if viewport: + # get_visible_rect() gives the coordinate space for 2D content + # With canvas_items + expand, this is the expanded design space var vp_size = viewport.get_visible_rect().size - # Hand cards are 195x273px (triple original), positioned at y=0 in container - var card_height = 273.0 - var hand_height = card_height + 25.0 # Extra space for hover lift + var is_min = hand_display and hand_display.is_minimized - # Position so card bottom is 100px above viewport bottom - var bottom_offset = 100.0 - hand_display.position = Vector2(10, vp_size.y - bottom_offset - card_height) - hand_display.size = Vector2(vp_size.x - 20, hand_height) + var card_height = 273.0 + if is_min: + # Minimized: push cards down so only the top portion peeks up + var peek_height = 65.0 # How much of the card top is visible + hand_display.position = Vector2(10, vp_size.y - peek_height) + hand_display.size = Vector2(vp_size.x - 20, card_height + 25.0) + else: + # Full hand: cards at the bottom of the screen + var hand_height = card_height + 25.0 # Extra space for hover lift + var bottom_offset = 10.0 + hand_display.position = Vector2(10, vp_size.y - bottom_offset - card_height) + hand_display.size = Vector2(vp_size.x - 20, hand_height) if not viewport.size_changed.is_connected(_on_viewport_resized): viewport.size_changed.connect(_on_viewport_resized) @@ -108,12 +116,10 @@ func _position_hand_display() -> void: func _on_viewport_resized() -> void: var viewport = get_viewport() if viewport and hand_display: - var vp_size = viewport.get_visible_rect().size - var card_height = 273.0 # Triple size hand cards - var hand_height = card_height + 25.0 - var bottom_offset = 100.0 - hand_display.position = Vector2(10, vp_size.y - bottom_offset - card_height) - hand_display.size = Vector2(vp_size.x - 20, hand_height) + _position_hand_display() + +func _on_hand_minimized_changed(_minimized: bool) -> void: + _position_hand_display() func _connect_signals() -> void: # GameManager signals diff --git a/scripts/ui/HandDisplay.gd b/scripts/ui/HandDisplay.gd index b01768f..05ce20a 100644 --- a/scripts/ui/HandDisplay.gd +++ b/scripts/ui/HandDisplay.gd @@ -8,6 +8,7 @@ signal card_action_requested(card_instance: CardInstance, action: String) signal card_hovered(card_instance: CardInstance) signal card_unhovered signal card_selected(card_instance: CardInstance) # Emitted when selection panel opens +signal hand_minimized_changed(minimized: bool) # Emitted when minimize state changes # Hand card visuals var hand_cards: Array[Control] = [] @@ -32,6 +33,14 @@ var selected_card_panel: Panel = null var card_image_container: Control = null var action_menu: VBoxContainer = null +# Minimized hand state +var is_minimized: bool = false +var minimized_bar: HBoxContainer = null +var minimized_items: Array = [] +var toggle_button: Button = null +const MINIMIZED_BAR_HEIGHT: float = 30.0 +const MINIMIZED_ITEM_GAP: float = 4.0 + # Debug visualization (set to true to see positioning markers) const DEBUG_MARKERS: bool = false var debug_container_outline: ColorRect = null @@ -41,6 +50,8 @@ func _ready() -> void: clip_contents = false # Allow cards to extend beyond container bounds resized.connect(_on_resized) _create_selection_ui() + _create_toggle_button() + _create_minimized_bar() if DEBUG_MARKERS: _create_debug_markers() @@ -91,6 +102,175 @@ func _create_selection_ui() -> void: selected_card_panel.size = Vector2(panel_width, panel_height) +func _create_toggle_button() -> void: + toggle_button = Button.new() + toggle_button.text = "Hide Hand" + toggle_button.custom_minimum_size = Vector2(90, 26) + toggle_button.size = Vector2(90, 26) + toggle_button.mouse_filter = Control.MOUSE_FILTER_STOP + toggle_button.z_index = 150 + + toggle_button.add_theme_font_size_override("font_size", 11) + toggle_button.add_theme_color_override("font_color", Color(0.8, 0.75, 0.65)) + toggle_button.add_theme_color_override("font_hover_color", Color(1.0, 0.95, 0.8)) + + var style_normal = StyleBoxFlat.new() + style_normal.bg_color = Color(0.12, 0.12, 0.16, 0.85) + style_normal.border_color = Color(0.4, 0.35, 0.25) + style_normal.set_border_width_all(1) + style_normal.set_corner_radius_all(4) + toggle_button.add_theme_stylebox_override("normal", style_normal) + + var style_hover = StyleBoxFlat.new() + style_hover.bg_color = Color(0.18, 0.18, 0.24, 0.9) + style_hover.border_color = Color(0.6, 0.5, 0.3) + style_hover.set_border_width_all(1) + style_hover.set_corner_radius_all(4) + toggle_button.add_theme_stylebox_override("hover", style_hover) + + var style_pressed = StyleBoxFlat.new() + style_pressed.bg_color = Color(0.08, 0.08, 0.1, 0.9) + style_pressed.border_color = Color(0.6, 0.5, 0.3) + style_pressed.set_border_width_all(1) + style_pressed.set_corner_radius_all(4) + toggle_button.add_theme_stylebox_override("pressed", style_pressed) + + toggle_button.pressed.connect(_on_toggle_hand) + add_child(toggle_button) + +func _create_minimized_bar() -> void: + minimized_bar = HBoxContainer.new() + minimized_bar.visible = false + minimized_bar.mouse_filter = Control.MOUSE_FILTER_IGNORE + minimized_bar.add_theme_constant_override("separation", int(MINIMIZED_ITEM_GAP)) + minimized_bar.z_index = 10 + add_child(minimized_bar) + +func _on_toggle_hand() -> void: + is_minimized = not is_minimized + if is_minimized: + toggle_button.text = "Show Hand" + _deselect_card() + # Cards stay visible — Main.gd will reposition so only tops peek up + else: + toggle_button.text = "Hide Hand" + _layout_cards() + _position_toggle_button() + hand_minimized_changed.emit(is_minimized) + +func _update_minimized_bar() -> void: + # Clear previous items + for child in minimized_bar.get_children(): + child.queue_free() + minimized_items.clear() + + for i in range(card_instances.size()): + var card = card_instances[i] + var item = _create_minimized_item(card, i) + minimized_bar.add_child(item) + minimized_items.append(item) + + # Position the bar centered at the bottom + call_deferred("_position_minimized_bar") + +func _create_minimized_item(card: CardInstance, index: int) -> PanelContainer: + var item = PanelContainer.new() + item.mouse_filter = Control.MOUSE_FILTER_STOP + + var style = StyleBoxFlat.new() + style.bg_color = Color(0.1, 0.1, 0.14, 0.9) + style.border_color = Color(0.3, 0.3, 0.35) + style.set_border_width_all(1) + style.set_corner_radius_all(3) + style.content_margin_left = 4 + style.content_margin_right = 8 + style.content_margin_top = 2 + style.content_margin_bottom = 2 + item.add_theme_stylebox_override("panel", style) + + var hbox = HBoxContainer.new() + hbox.add_theme_constant_override("separation", 5) + hbox.mouse_filter = Control.MOUSE_FILTER_IGNORE + item.add_child(hbox) + + # Cost crystal — element-colored circle with cost number + var crystal = Panel.new() + crystal.custom_minimum_size = Vector2(22, 22) + crystal.mouse_filter = Control.MOUSE_FILTER_IGNORE + var crystal_style = StyleBoxFlat.new() + var elem_color = Enums.get_element_color(card.card_data.get_primary_element()) + crystal_style.bg_color = elem_color + crystal_style.set_corner_radius_all(11) + crystal_style.set_border_width_all(1) + crystal_style.border_color = elem_color.lightened(0.3) + crystal.add_theme_stylebox_override("panel", crystal_style) + + var cost_label = Label.new() + cost_label.text = str(card.card_data.cost) + cost_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + cost_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + cost_label.set_anchors_preset(Control.PRESET_FULL_RECT) + cost_label.add_theme_font_size_override("font_size", 12) + cost_label.add_theme_color_override("font_color", Color.WHITE) + cost_label.mouse_filter = Control.MOUSE_FILTER_IGNORE + crystal.add_child(cost_label) + hbox.add_child(crystal) + + # Card name + var name_label = Label.new() + name_label.text = card.card_data.name + name_label.add_theme_font_size_override("font_size", 12) + name_label.add_theme_color_override("font_color", Color(0.85, 0.82, 0.72)) + name_label.mouse_filter = Control.MOUSE_FILTER_IGNORE + hbox.add_child(name_label) + + # Click to expand hand and select this card + item.gui_input.connect(_on_minimized_item_input.bind(index)) + + return item + +func _on_minimized_item_input(event: InputEvent, index: int) -> void: + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: + # Expand hand and select the clicked card + is_minimized = false + toggle_button.text = "Hide Hand" + minimized_bar.visible = false + for card_ui in hand_cards: + card_ui.visible = true + _layout_cards() + _position_toggle_button() + hand_minimized_changed.emit(false) + _select_card(index) + +func _position_toggle_button() -> void: + if not toggle_button: + return + var display_width = size.x + if display_width <= 0: + var viewport = get_viewport() + if viewport: + display_width = viewport.get_visible_rect().size.x - 20 + # Position toggle button: right side, above hand area + if is_minimized: + # Above the minimized bar + toggle_button.position = Vector2(display_width - toggle_button.size.x - 5, -toggle_button.size.y - 4) + else: + # Above the full hand + toggle_button.position = Vector2(display_width - toggle_button.size.x - 5, -toggle_button.size.y - 4) + +func _position_minimized_bar() -> void: + if not minimized_bar: + return + var display_width = size.x + if display_width <= 0: + var viewport = get_viewport() + if viewport: + display_width = viewport.get_visible_rect().size.x - 20 + # Center the bar horizontally, positioned at top of hand container area + var bar_width = minimized_bar.size.x + minimized_bar.position = Vector2((display_width - bar_width) / 2.0, 0) + func _create_debug_markers() -> void: # Create a visible outline around the hand container bounds # RED = hand container area @@ -162,6 +342,8 @@ func update_hand(cards: Array) -> void: # Create card UI elements _create_card_visuals() + # Cards stay visible even when minimized — Main.gd handles positioning + # Defer layout to ensure size is set call_deferred("_layout_cards") @@ -234,6 +416,8 @@ func _get_element_color(element: Enums.Element) -> Color: return color.lightened(0.4) func _layout_cards() -> void: + _position_toggle_button() + var card_count = hand_cards.size() if card_count == 0: return @@ -258,8 +442,8 @@ func _layout_cards() -> void: var x = start_x + i * CARD_OVERLAP var y = base_y - # Lift hovered card slightly - if i == hovered_card_index and selected_card_index == -1: + # Lift hovered card slightly (not when minimized) + if i == hovered_card_index and selected_card_index == -1 and not is_minimized: y -= HOVER_LIFT # Lift by 15px # Dim selected card in hand @@ -281,7 +465,15 @@ func _on_card_gui_input(event: InputEvent, index: int) -> void: if event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: if index >= 0 and index < card_instances.size(): - if selected_card_index == index: + if is_minimized: + # Clicking a peeking card expands the hand and selects it + is_minimized = false + toggle_button.text = "Hide Hand" + hand_minimized_changed.emit(false) + _layout_cards() + _position_toggle_button() + _select_card(index) + elif selected_card_index == index: _deselect_card() else: _select_card(index)