diff --git a/ui/components/bubble_menu/bubble_content_shell.gd b/ui/components/bubble_menu/bubble_content_shell.gd new file mode 100644 index 0000000..f20bfb5 --- /dev/null +++ b/ui/components/bubble_menu/bubble_content_shell.gd @@ -0,0 +1,49 @@ +class_name BubbleContentShell +extends PanelContainer + +@export var profile: BubbleMenuProfile +@export_range(0.0, 96.0, 1.0) var content_margin: float = 24.0 +@export_range(0, 512, 1) var corner_radius: int = 72 +@export_range(0, 12, 1) var border_width: int = 3 + +var _background_visible: bool = true + + +func _ready() -> void: + apply_profile() + + +func apply_profile() -> void: + if profile == null: + return + var style := StyleBoxFlat.new() + style.bg_color = profile.normal_fill + style.border_color = profile.normal_border + style.border_width_left = border_width + style.border_width_top = border_width + style.border_width_right = border_width + style.border_width_bottom = border_width + style.corner_radius_top_left = corner_radius + style.corner_radius_top_right = corner_radius + style.corner_radius_bottom_right = corner_radius + style.corner_radius_bottom_left = corner_radius + style.content_margin_left = content_margin + style.content_margin_top = content_margin + style.content_margin_right = content_margin + style.content_margin_bottom = content_margin + add_theme_stylebox_override("panel", style) + + +func set_background_visible(background_visible: bool) -> void: + _background_visible = background_visible + if not is_node_ready() or profile == null: + return + if _background_visible: + apply_profile() + return + var transparent_style := StyleBoxEmpty.new() + transparent_style.content_margin_left = content_margin + transparent_style.content_margin_top = content_margin + transparent_style.content_margin_right = content_margin + transparent_style.content_margin_bottom = content_margin + add_theme_stylebox_override("panel", transparent_style) diff --git a/ui/components/bubble_menu/bubble_content_shell.gd.uid b/ui/components/bubble_menu/bubble_content_shell.gd.uid new file mode 100644 index 0000000..fbbdbbf --- /dev/null +++ b/ui/components/bubble_menu/bubble_content_shell.gd.uid @@ -0,0 +1 @@ +uid://bsplayercontentshell diff --git a/ui/components/bubble_menu/bubble_content_shell.tscn b/ui/components/bubble_menu/bubble_content_shell.tscn new file mode 100644 index 0000000..2feac61 --- /dev/null +++ b/ui/components/bubble_menu/bubble_content_shell.tscn @@ -0,0 +1,8 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_content_shell.gd" id="1_shell"] +[ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="2_profile"] + +[node name="BubbleContentShell" type="PanelContainer"] +script = ExtResource("1_shell") +profile = ExtResource("2_profile") diff --git a/ui/components/bubble_menu/bubble_information_panel.gd b/ui/components/bubble_menu/bubble_information_panel.gd new file mode 100644 index 0000000..22989a9 --- /dev/null +++ b/ui/components/bubble_menu/bubble_information_panel.gd @@ -0,0 +1,21 @@ +class_name BubbleInformationPanel +extends PanelContainer + +@export var profile: BubbleMenuProfile +@export_range(0.0, 96.0, 1.0) var content_margin: float = 20.0 +@export_range(0, 512, 1) var corner_radius: int = 96 + + +func _ready() -> void: + if profile == null: + return + var style: StyleBoxFlat = profile.make_normal_style() + style.corner_radius_top_left = corner_radius + style.corner_radius_top_right = corner_radius + style.corner_radius_bottom_left = corner_radius + style.corner_radius_bottom_right = corner_radius + style.content_margin_left = content_margin + style.content_margin_top = content_margin + style.content_margin_right = content_margin + style.content_margin_bottom = content_margin + add_theme_stylebox_override("panel", style) diff --git a/ui/components/bubble_menu/bubble_information_panel.gd.uid b/ui/components/bubble_menu/bubble_information_panel.gd.uid new file mode 100644 index 0000000..7586d5c --- /dev/null +++ b/ui/components/bubble_menu/bubble_information_panel.gd.uid @@ -0,0 +1 @@ +uid://bubbleinformationpanel diff --git a/ui/components/bubble_menu/bubble_information_panel.tscn b/ui/components/bubble_menu/bubble_information_panel.tscn new file mode 100644 index 0000000..df6619c --- /dev/null +++ b/ui/components/bubble_menu/bubble_information_panel.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_information_panel.gd" id="1_panel"] +[ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="2_profile"] + +[node name="BubbleInformationPanel" type="PanelContainer"] +mouse_filter = 1 +script = ExtResource("1_panel") +profile = ExtResource("2_profile") diff --git a/ui/components/bubble_menu/bubble_inventory_cell.gd b/ui/components/bubble_menu/bubble_inventory_cell.gd new file mode 100644 index 0000000..96e14aa --- /dev/null +++ b/ui/components/bubble_menu/bubble_inventory_cell.gd @@ -0,0 +1,125 @@ +class_name BubbleInventoryCell +extends Button + +@export var profile: BubbleMenuProfile + +@onready var _icon: TextureRect = %Icon +@onready var _primary_label: Label = %PrimaryLabel +@onready var _secondary_label: Label = %SecondaryLabel +@onready var _favorite_marker: Label = %FavoriteMarker +@onready var _selected_marker: Label = %SelectedMarker + +var _selected: bool = false +var _focused_item: bool = false +var _compact: bool = false +var _icon_texture: Texture2D +var _primary_text: String = "" +var _secondary_text: String = "" +var _favorite: bool = false + + +func _ready() -> void: + focus_entered.connect(_refresh_style) + focus_exited.connect(_refresh_style) + _apply_compact_layout() + _apply_content() + _refresh_style() + + +func configure( + icon_texture: Texture2D, + primary_text: String, + secondary_text: String, +) -> void: + _icon_texture = icon_texture + _primary_text = primary_text + _secondary_text = secondary_text + if is_node_ready(): + _apply_content() + + +func set_item_state( + selected: bool, + focused_item: bool, + favorite: bool, +) -> void: + _selected = selected + _focused_item = focused_item + _favorite = favorite + if is_node_ready(): + _apply_content() + _refresh_style() + + +func set_compact(compact: bool) -> void: + _compact = compact + if not is_node_ready(): + custom_minimum_size = ( + Vector2(104.0, 98.0) + if compact + else Vector2(144.0, 136.0) + ) + return + _apply_compact_layout() + + +func _apply_compact_layout() -> void: + custom_minimum_size = ( + Vector2(104.0, 98.0) + if _compact + else Vector2(144.0, 136.0) + ) + _icon.custom_minimum_size = ( + Vector2(72.0, 44.0) + if _compact + else Vector2(104.0, 68.0) + ) + _primary_label.add_theme_font_size_override( + "font_size", + 15 if _compact else 18, + ) + _secondary_label.add_theme_font_size_override( + "font_size", + 13 if _compact else 15, + ) + + +func _apply_content() -> void: + _icon.texture = _icon_texture + _primary_label.text = _primary_text + _secondary_label.text = _secondary_text + _selected_marker.visible = _selected + _favorite_marker.visible = _favorite + + +func _has_point(point: Vector2) -> bool: + var radius: Vector2 = size * 0.5 + if radius.x <= 0.0 or radius.y <= 0.0: + return false + var normalized: Vector2 = (point - radius) / radius + return normalized.length_squared() <= 1.0 + + +func _refresh_style() -> void: + if profile == null: + return + var normal_style: StyleBoxFlat = profile.make_normal_style() + var border_width: int = ( + profile.emphasized_border_width + if _selected or _focused_item or has_focus() + else profile.normal_border_width + ) + normal_style.border_width_left = border_width + normal_style.border_width_top = border_width + normal_style.border_width_right = border_width + normal_style.border_width_bottom = border_width + if _selected: + normal_style.bg_color = profile.pressed_fill + if _focused_item: + normal_style.border_color = profile.text_color + add_theme_stylebox_override("normal", normal_style) + add_theme_stylebox_override("hover", profile.make_hover_style()) + add_theme_stylebox_override("focus", normal_style) + add_theme_stylebox_override("pressed", profile.make_pressed_style()) + add_theme_stylebox_override("disabled", profile.make_disabled_style()) + add_theme_color_override("font_color", profile.text_color) diff --git a/ui/components/bubble_menu/bubble_inventory_cell.gd.uid b/ui/components/bubble_menu/bubble_inventory_cell.gd.uid new file mode 100644 index 0000000..5455c81 --- /dev/null +++ b/ui/components/bubble_menu/bubble_inventory_cell.gd.uid @@ -0,0 +1 @@ +uid://bubbleinventorycell diff --git a/ui/components/bubble_menu/bubble_inventory_cell.tscn b/ui/components/bubble_menu/bubble_inventory_cell.tscn new file mode 100644 index 0000000..d6774a3 --- /dev/null +++ b/ui/components/bubble_menu/bubble_inventory_cell.tscn @@ -0,0 +1,78 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_inventory_cell.gd" id="1_cell"] +[ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="2_profile"] + +[node name="BubbleInventoryCell" type="Button"] +custom_minimum_size = Vector2(144, 136) +toggle_mode = true +script = ExtResource("1_cell") +profile = ExtResource("2_profile") + +[node name="Content" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 17.0 +offset_top = 14.0 +offset_right = -17.0 +offset_bottom = -14.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme_override_constants/separation = 2 + +[node name="Icon" type="TextureRect" parent="Content"] +unique_name_in_owner = true +custom_minimum_size = Vector2(104, 68) +layout_mode = 2 +size_flags_vertical = 3 +mouse_filter = 2 +texture_filter = 1 +expand_mode = 1 +stretch_mode = 5 + +[node name="PrimaryLabel" type="Label" parent="Content"] +unique_name_in_owner = true +layout_mode = 2 +mouse_filter = 2 +theme_override_font_sizes/font_size = 18 +horizontal_alignment = 1 +text_overrun_behavior = 3 + +[node name="SecondaryLabel" type="Label" parent="Content"] +unique_name_in_owner = true +layout_mode = 2 +mouse_filter = 2 +theme_override_colors/font_color = Color(0.16, 0.25, 0.29, 0.78) +theme_override_font_sizes/font_size = 15 +horizontal_alignment = 1 + +[node name="FavoriteMarker" type="Label" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 0 +offset_left = 102.0 +offset_top = 12.0 +offset_right = 132.0 +offset_bottom = 42.0 +mouse_filter = 2 +theme_override_colors/font_color = Color(1, 0.82, 0.4, 1) +theme_override_font_sizes/font_size = 23 +text = "★" +horizontal_alignment = 1 + +[node name="SelectedMarker" type="Label" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 0 +offset_left = 14.0 +offset_top = 10.0 +offset_right = 42.0 +offset_bottom = 38.0 +mouse_filter = 2 +theme_override_colors/font_color = Color(0.035, 0.145, 0.22, 1) +theme_override_font_sizes/font_size = 22 +text = "✓" +horizontal_alignment = 1 diff --git a/ui/components/bubble_menu/bubble_status_bubble.gd b/ui/components/bubble_menu/bubble_status_bubble.gd new file mode 100644 index 0000000..2a475fc --- /dev/null +++ b/ui/components/bubble_menu/bubble_status_bubble.gd @@ -0,0 +1,18 @@ +class_name BubbleStatusBubble +extends PanelContainer + +@export var profile: BubbleMenuProfile + +@onready var _heading: Label = %Heading +@onready var _value: Label = %Value + + +func _ready() -> void: + mouse_filter = Control.MOUSE_FILTER_IGNORE + if profile != null: + add_theme_stylebox_override("panel", profile.make_normal_style()) + + +func set_content(heading: String, value: String) -> void: + _heading.text = heading + _value.text = value diff --git a/ui/components/bubble_menu/bubble_status_bubble.gd.uid b/ui/components/bubble_menu/bubble_status_bubble.gd.uid new file mode 100644 index 0000000..b67ba57 --- /dev/null +++ b/ui/components/bubble_menu/bubble_status_bubble.gd.uid @@ -0,0 +1 @@ +uid://bubblestatusbubble diff --git a/ui/components/bubble_menu/bubble_status_bubble.tscn b/ui/components/bubble_menu/bubble_status_bubble.tscn new file mode 100644 index 0000000..87858ec --- /dev/null +++ b/ui/components/bubble_menu/bubble_status_bubble.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_status_bubble.gd" id="1_status"] +[ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="2_profile"] + +[node name="BubbleStatusBubble" type="PanelContainer"] +custom_minimum_size = Vector2(128, 88) +mouse_filter = 2 +script = ExtResource("1_status") +profile = ExtResource("2_profile") + +[node name="Labels" type="VBoxContainer" parent="."] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/separation = 0 +alignment = 1 + +[node name="Heading" type="Label" parent="Labels"] +unique_name_in_owner = true +layout_mode = 2 +mouse_filter = 2 +theme_override_font_sizes/font_size = 15 +text = "status" +horizontal_alignment = 1 + +[node name="Value" type="Label" parent="Labels"] +unique_name_in_owner = true +layout_mode = 2 +mouse_filter = 2 +theme_override_font_sizes/font_size = 21 +text = "0" +horizontal_alignment = 1 diff --git a/ui/player_menu.gd b/ui/player_menu.gd index bc7d207..9ebc27c 100644 --- a/ui/player_menu.gd +++ b/ui/player_menu.gd @@ -25,6 +25,30 @@ const PlayerCoolerCapacityType = preload( const FishBatchSelectionType = preload( "res://ui/fish_batch_selection.gd" ) +const BubbleButtonType = preload( + "res://ui/components/bubble_menu/bubble_button.gd" +) +const BubbleClusterType = preload( + "res://ui/components/bubble_menu/bubble_cluster.gd" +) +const BubbleContentShellType = preload( + "res://ui/components/bubble_menu/bubble_content_shell.gd" +) +const BubbleInventoryCellType = preload( + "res://ui/components/bubble_menu/bubble_inventory_cell.gd" +) +const BubbleInventoryCellScene = preload( + "res://ui/components/bubble_menu/bubble_inventory_cell.tscn" +) +const BubbleStatusBubbleType = preload( + "res://ui/components/bubble_menu/bubble_status_bubble.gd" +) + +const MENU_ENTER_DURATION: float = 0.58 +const MENU_EXIT_DURATION: float = 0.52 +const PAGE_OUT_DURATION: float = 0.34 +const PAGE_IN_DURATION: float = 0.42 +const TRANSITION_SAFE_MARGIN: float = 28.0 signal menu_visibility_changed(is_open: bool) @@ -49,16 +73,32 @@ enum CloseReason { TEARDOWN, } -@onready var _inventory_tab: Button = %InventoryTab -@onready var _bag_tab: Button = %BagTab -@onready var _logbook_tab: Button = %LogbookTab -@onready var _close_button: Button = %CloseButton +@onready var _navigation_cluster: BubbleClusterType = %NavigationCluster +@onready var _inventory_tab: BubbleButtonType = %InventoryTab +@onready var _bag_tab: BubbleButtonType = %BagTab +@onready var _logbook_tab: BubbleButtonType = %LogbookTab +@onready var _close_button: BubbleButtonType = %CloseButton +@onready var _content_shell: BubbleContentShellType = %MenuPanel +@onready var _content_stage: Control = %Content +@onready var _cooler_scroll: ScrollContainer = %CoolerScroll +@onready var _cooler_host: VBoxContainer = %CoolerHost @onready var _wallet_balance: Label = %WalletBalance +@onready var _header: Control = %Header +@onready var _separator: Control = %Separator +@onready var _wallet_status: BubbleStatusBubbleType = %WalletStatus +@onready var _capacity_status: BubbleStatusBubbleType = %CapacityStatus +@onready var _held_value_status: BubbleStatusBubbleType = %HeldValueStatus +@onready var _selection_status: BubbleStatusBubbleType = %SelectionStatus +@onready var _offer_status: BubbleStatusBubbleType = %OfferStatus @onready var _inventory_section: Control = %InventorySection +@onready var _inventory_body: BoxContainer = %InventoryBody +@onready var _inventory_scroll: ScrollContainer = %InventoryScroll @onready var _bag_section: Control = %BagSection @onready var _logbook_section: Control = %LogbookSection @onready var _bag_empty: Label = %BagEmpty @onready var _bag_grid: GridContainer = %BagGrid +@onready var _bag_list: Control = %BagList +@onready var _bag_detail: Control = %BagDetail @onready var _bag_detail_texture: TextureRect = %BagDetailTexture @onready var _bag_detail_name: Label = %BagDetailName @onready var _bag_detail_data: Label = %BagDetailData @@ -68,6 +108,8 @@ enum CloseReason { @onready var _cooler_count: Label = %CoolerCount @onready var _inventory_empty: Label = %InventoryEmpty @onready var _inventory_grid: GridContainer = %InventoryGrid +@onready var _inventory_list: Control = %InventoryList +@onready var _detail_panel: Control = %DetailPanel @onready var _detail_texture: TextureRect = %DetailTexture @onready var _detail_name: Label = %DetailName @onready var _detail_data: Label = %DetailData @@ -82,7 +124,10 @@ enum CloseReason { @onready var _cancel_sale_button: Button = %CancelSaleButton @onready var _logbook_empty: Label = %LogbookEmpty @onready var _logbook_grid: GridContainer = %LogbookGrid +@onready var _status_shoal: HBoxContainer = %StatusShoal +@onready var _sort_bar: HBoxContainer = %SortBar +var _compact_layout: bool = false var _player: PlayerType var _inventory: FishInventoryType var _collection_log: CollectionLogType @@ -111,9 +156,24 @@ var _confirmation_buyer: FishBuyerProfileType var _confirmation_buyer_id: StringName var _confirmation_generation: int = -1 var _sale_in_progress: bool = false +var _presentation_tween: Tween +var _page_tween: Tween +var _transition_generation: int = 0 +var _page_transition_generation: int = 0 +var _transitioning: bool = false +var _page_transitioning: bool = false +var _presentation_rest_position: Vector2 = Vector2.ZERO +var _content_rest_position: Vector2 = Vector2.ZERO func _ready() -> void: + _inventory_section.reparent(_cooler_host) + _inventory_section.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _inventory_section.size_flags_vertical = Control.SIZE_EXPAND_FILL + _inventory_section.move_child( + _sort_bar, + _inventory_section.get_child_count() - 1, + ) _inventory_tab.pressed.connect( _show_section.bind(Section.COOLER) ) @@ -133,7 +193,18 @@ func _ready() -> void: _sort_option.add_item("rarity", SortMode.RARITY) _sort_option.select(SortMode.CATCH_ORDER) _update_sort_direction_text() - _show_section(_current_section) + _navigation_cluster.configure([ + _inventory_tab, + _bag_tab, + _logbook_tab, + _close_button, + ]) + _configure_navigation_focus() + _apply_cooler_control_styles() + resized.connect(_update_shell_layout) + _show_section_immediate(_current_section) + call_deferred("_update_shell_layout") + set_process(false) func setup( @@ -185,7 +256,8 @@ func _input(event: InputEvent) -> void: return if visible: if event.is_action_pressed("open_backpack"): - close_menu() + if not _transitioning and not _page_transitioning: + close_menu() get_viewport().set_input_as_handled() return if ( @@ -200,6 +272,8 @@ func _input(event: InputEvent) -> void: func consume_escape() -> bool: if not visible: return false + if _transitioning or _page_transitioning: + return true if _sale_confirmation.visible: _close_sale_confirmation() else: @@ -216,6 +290,9 @@ func open_menu() -> void: ): return _menu_generation += 1 + _transition_generation += 1 + _cancel_presentation_tween() + _cancel_page_tween() _prior_movement_enabled = _player.is_movement_enabled() _prior_camera_input_enabled = _player.is_camera_input_enabled() _prior_mouse_mode = Input.mouse_mode @@ -227,8 +304,9 @@ func open_menu() -> void: Input.mouse_mode = Input.MOUSE_MODE_VISIBLE visible = true _refresh_all() - _show_section(_current_section) - _focus_current_section() + _show_section_immediate(_current_section) + _update_shell_layout() + _begin_menu_entry() menu_visibility_changed.emit(true) @@ -238,6 +316,10 @@ func close_menu( ) -> void: if not visible: return + if _transitioning: + if reason != CloseReason.USER: + _finish_close(reason, restore_controls, _menu_generation) + return get_viewport().gui_cancel_drag() _close_sale_confirmation() if reason in [ @@ -247,18 +329,7 @@ func close_menu( CloseReason.TEARDOWN, ]: _fish_selection.clear() - var closing_generation: int = _menu_generation - visible = false - get_viewport().gui_release_focus() - if _fishing_spot != null and is_instance_valid(_fishing_spot): - _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false) - if restore_controls: - _restore_player_controls(closing_generation) - else: - _control_snapshot_stored = false - _apply_mouse_close_policy(reason) - _menu_generation += 1 - menu_visibility_changed.emit(false) + _begin_menu_exit(reason, restore_controls) func close_for_water_recovery() -> void: @@ -276,8 +347,10 @@ func close_for_session_end() -> void: func _exit_tree() -> void: + _cancel_presentation_tween() + _cancel_page_tween() if visible: - close_menu(CloseReason.TEARDOWN, false) + _finish_close(CloseReason.TEARDOWN, false, _menu_generation) _fish_selection.clear() _menu_generation += 1 @@ -319,17 +392,32 @@ func _on_bite_activated() -> void: func _show_section(section: Section) -> void: + if ( + section == _current_section + or _transitioning + or _page_transitioning + or _sale_confirmation.visible + or get_viewport().gui_is_dragging() + ): + return + _begin_page_transition(section) + + +func _show_section_immediate(section: Section) -> void: _current_section = section if not is_node_ready(): return _inventory_section.visible = section == Section.COOLER + _cooler_scroll.visible = section == Section.COOLER _bag_section.visible = section == Section.BAG _logbook_section.visible = section == Section.LOGBOOK + _content_shell.set_background_visible(section != Section.COOLER) + _header.visible = section != Section.COOLER + _separator.visible = section != Section.COOLER _inventory_tab.button_pressed = section == Section.COOLER _bag_tab.button_pressed = section == Section.BAG _logbook_tab.button_pressed = section == Section.LOGBOOK - if visible: - _focus_current_section() + _update_navigation_selection() func _focus_current_section() -> void: @@ -341,6 +429,346 @@ func _focus_current_section() -> void: _logbook_tab.grab_focus() +func _process(delta: float) -> void: + if visible: + _navigation_cluster.advance_motion(delta) + + +func _configure_navigation_focus() -> void: + var navigation: Array[BubbleButtonType] = [ + _inventory_tab, + _bag_tab, + _logbook_tab, + _close_button, + ] + for index: int in navigation.size(): + var bubble: BubbleButtonType = navigation[index] + var previous: BubbleButtonType = navigation[ + (index - 1 + navigation.size()) % navigation.size() + ] + var next: BubbleButtonType = navigation[ + (index + 1) % navigation.size() + ] + bubble.focus_neighbor_left = bubble.get_path_to(previous) + bubble.focus_neighbor_right = bubble.get_path_to(next) + bubble.focus_neighbor_top = bubble.focus_neighbor_left + bubble.focus_neighbor_bottom = bubble.focus_neighbor_right + + +func _apply_cooler_control_styles() -> void: + var profile: BubbleMenuProfile = _inventory_tab.profile + if profile == null: + return + _sort_option.add_theme_stylebox_override( + "normal", + profile.make_normal_style(), + ) + _sort_option.add_theme_stylebox_override( + "hover", + profile.make_hover_style(), + ) + _sort_option.add_theme_stylebox_override( + "focus", + profile.make_hover_style(), + ) + _sort_option.add_theme_stylebox_override( + "pressed", + profile.make_pressed_style(), + ) + _sort_option.add_theme_color_override("font_color", profile.text_color) + _sort_option.add_theme_color_override( + "font_hover_color", + profile.text_hover_color, + ) + _sort_option.add_theme_color_override( + "font_focus_color", + profile.text_hover_color, + ) + + +func _update_navigation_selection() -> void: + _set_navigation_target(_current_section) + + +func _set_navigation_target(section: Section) -> void: + _inventory_tab.button_pressed = section == Section.COOLER + _bag_tab.button_pressed = section == Section.BAG + _logbook_tab.button_pressed = section == Section.LOGBOOK + + +func _update_shell_layout() -> void: + if not is_node_ready(): + return + var compact: bool = size.y < 560.0 or size.x < 760.0 + _compact_layout = compact + var side_margin: float = 10.0 if compact else 42.0 + var top_margin: float = 96.0 if compact else 104.0 + var bottom_margin: float = 96.0 if compact else 138.0 + _content_shell.position = Vector2(side_margin, top_margin) + _content_shell.size = Vector2( + maxf(1.0, size.x - side_margin * 2.0), + maxf(1.0, size.y - top_margin - bottom_margin) + ) + _presentation_rest_position = _content_shell.position + var navigation_size := Vector2( + minf(size.x - 20.0, 610.0 if compact else 760.0), + 52.0 if compact else 96.0 + ) + _navigation_cluster.position = Vector2( + (size.x - navigation_size.x) * 0.5, + 4.0 if compact else 14.0 + ) + _navigation_cluster.size = navigation_size + _navigation_cluster.apply_layout(navigation_size, compact) + _inventory_grid.columns = 2 if compact else 3 + _bag_grid.columns = 2 if compact else 3 + _logbook_grid.columns = 3 if compact else 4 + _inventory_list.custom_minimum_size.x = 292.0 if compact else 350.0 + _inventory_list.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _detail_panel.custom_minimum_size.x = 176.0 if compact else 210.0 + _bag_list.custom_minimum_size.x = 300.0 if compact else 360.0 + _bag_detail.custom_minimum_size.x = 176.0 if compact else 220.0 + _content_stage.custom_minimum_size.y = 220.0 if compact else 260.0 + call_deferred("_sync_cooler_width") + _inventory_body.vertical = compact + _inventory_scroll.vertical_scroll_mode = ( + ScrollContainer.SCROLL_MODE_DISABLED + if compact + else ScrollContainer.SCROLL_MODE_AUTO + ) + _inventory_list.custom_minimum_size.y = 240.0 if compact else 0.0 + _detail_panel.custom_minimum_size = ( + Vector2(0.0, 310.0) + if compact + else Vector2(430.0, 0.0) + ) + _status_shoal.add_theme_constant_override( + "separation", + 4 if compact else 8, + ) + _inventory_body.queue_sort() + _content_rest_position = _content_stage.position + if not _transitioning: + _content_shell.position = _presentation_rest_position + if not _page_transitioning: + _content_stage.position = _content_rest_position + + +func _sync_cooler_width() -> void: + if not is_node_ready(): + return + _cooler_host.custom_minimum_size.x = minf( + _content_stage.size.x, + 1380.0, + ) + _cooler_host.size_flags_horizontal = Control.SIZE_SHRINK_CENTER + + +func _begin_menu_entry() -> void: + _transitioning = true + _set_shell_interactive(false) + set_process(true) + var generation: int = _transition_generation + var start_offset: float = size.y + TRANSITION_SAFE_MARGIN + _content_shell.position = _presentation_rest_position + Vector2.DOWN * start_offset + var navigation_rest: Vector2 = _navigation_cluster.position + _navigation_cluster.position = navigation_rest + Vector2.DOWN * start_offset + _presentation_tween = create_tween().set_parallel(true) + _presentation_tween.tween_property( + _content_shell, + "position", + _presentation_rest_position, + MENU_ENTER_DURATION + ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _presentation_tween.tween_property( + _navigation_cluster, + "position", + navigation_rest, + MENU_ENTER_DURATION + ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _presentation_tween.chain().tween_callback( + _finish_menu_entry.bind(generation) + ) + + +func _finish_menu_entry(generation: int) -> void: + if generation != _transition_generation or not visible: + return + _presentation_tween = null + _transitioning = false + _set_shell_interactive(true) + _focus_current_section() + + +func _begin_menu_exit(reason: CloseReason, restore_controls: bool) -> void: + if reason != CloseReason.USER: + _finish_close(reason, restore_controls, _menu_generation) + return + _transition_generation += 1 + _cancel_presentation_tween() + _transitioning = true + _set_shell_interactive(false) + get_viewport().gui_release_focus() + var generation: int = _transition_generation + var closing_generation: int = _menu_generation + var end_y: float = -maxf( + _content_shell.size.y, + _navigation_cluster.size.y + ) - TRANSITION_SAFE_MARGIN + _presentation_tween = create_tween().set_parallel(true) + _presentation_tween.tween_property( + _content_shell, + "position:y", + end_y, + MENU_EXIT_DURATION + ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _presentation_tween.tween_property( + _navigation_cluster, + "position:y", + -_navigation_cluster.size.y - TRANSITION_SAFE_MARGIN, + MENU_EXIT_DURATION + ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _presentation_tween.chain().tween_callback( + _finish_menu_exit.bind( + generation, + reason, + restore_controls, + closing_generation, + ) + ) + + +func _finish_menu_exit( + generation: int, + reason: CloseReason, + restore_controls: bool, + closing_generation: int, +) -> void: + if generation != _transition_generation or not visible: + return + _presentation_tween = null + _finish_close(reason, restore_controls, closing_generation) + + +func _finish_close( + reason: CloseReason, + restore_controls: bool, + closing_generation: int, +) -> void: + _cancel_presentation_tween() + _cancel_page_tween() + _transitioning = false + _page_transitioning = false + visible = false + set_process(false) + get_viewport().gui_release_focus() + if _fishing_spot != null and is_instance_valid(_fishing_spot): + _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false) + if restore_controls: + _restore_player_controls(closing_generation) + else: + _control_snapshot_stored = false + _apply_mouse_close_policy(reason) + _menu_generation += 1 + _transition_generation += 1 + menu_visibility_changed.emit(false) + + +func _begin_page_transition(section: Section) -> void: + _page_transition_generation += 1 + _cancel_page_tween() + _page_transitioning = true + _set_content_interactive(false) + _set_navigation_target(section) + var generation: int = _page_transition_generation + _page_tween = create_tween() + _page_tween.tween_property( + _content_stage, + "modulate:a", + 0.0, + PAGE_OUT_DURATION + ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _page_tween.parallel().tween_property( + _content_stage, + "position:y", + _content_rest_position.y - 18.0, + PAGE_OUT_DURATION + ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _page_tween.tween_callback( + _swap_page.bind(section, generation) + ) + _page_tween.tween_property( + _content_stage, + "modulate:a", + 1.0, + PAGE_IN_DURATION + ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _page_tween.parallel().tween_property( + _content_stage, + "position", + _content_rest_position, + PAGE_IN_DURATION + ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _page_tween.finished.connect( + _finish_page_transition.bind(generation), + CONNECT_ONE_SHOT + ) + + +func _swap_page(section: Section, generation: int) -> void: + if generation != _page_transition_generation or not visible: + return + _show_section_immediate(section) + _content_stage.position = _content_rest_position + Vector2.DOWN * 18.0 + + +func _finish_page_transition(generation: int) -> void: + if generation != _page_transition_generation or not visible: + return + _page_tween = null + _page_transitioning = false + _content_stage.position = _content_rest_position + _content_stage.modulate.a = 1.0 + _set_content_interactive(true) + _focus_current_section() + + +func _set_shell_interactive(interactive: bool) -> void: + _set_content_interactive(interactive) + for bubble: BubbleButtonType in [ + _inventory_tab, + _bag_tab, + _logbook_tab, + _close_button, + ]: + bubble.focus_mode = Control.FOCUS_ALL if interactive else Control.FOCUS_NONE + bubble.mouse_filter = ( + Control.MOUSE_FILTER_STOP + if interactive + else Control.MOUSE_FILTER_IGNORE + ) + + +func _set_content_interactive(interactive: bool) -> void: + _content_stage.mouse_filter = ( + Control.MOUSE_FILTER_PASS + if interactive + else Control.MOUSE_FILTER_IGNORE + ) + + +func _cancel_presentation_tween() -> void: + if _presentation_tween != null: + _presentation_tween.kill() + _presentation_tween = null + + +func _cancel_page_tween() -> void: + if _page_tween != null: + _page_tween.kill() + _page_tween = null + + func _on_sort_selected(index: int) -> void: _sort_mode = _sort_option.get_item_id(index) _refresh_inventory() @@ -486,6 +914,12 @@ func _refresh_economy_summary() -> void: else 0 ) _wallet_balance.text = "wallet: $%d" % balance + _wallet_status.set_content("wallet", "$%d" % balance) + _capacity_status.set_content("cooler", "%d / %d" % [ + _inventory.get_all_catches().size() if _inventory != null else 0, + _cooler_capacity.get_capacity() if _cooler_capacity != null else 0, + ]) + _held_value_status.set_content("held value", "$%d" % held_total) _held_value.text = "held fish base value: $%d" % held_total _cooler_count.text = "%d / %d" % [ _inventory.get_all_catches().size() if _inventory != null else 0, @@ -518,11 +952,32 @@ func _refresh_inventory() -> void: selected = _inventory.get_catch(_fish_selection.get_focused_id()) for fish_catch: FishCatchType in catches: _inventory_grid.add_child(_create_inventory_card(fish_catch)) + _configure_inventory_card_focus() _update_inventory_detail(selected) _update_sale_summary() _update_sort_direction_text() +func _configure_inventory_card_focus() -> void: + var cards: Array[Control] = [] + for child: Node in _inventory_grid.get_children(): + if child is Control: + cards.append(child as Control) + var columns: int = maxi(_inventory_grid.columns, 1) + for index: int in cards.size(): + var card: Control = cards[index] + var row: int = floori(float(index) / float(columns)) + var column: int = index % columns + var left_index: int = row * columns + maxi(column - 1, 0) + var right_index: int = mini(index + 1, cards.size() - 1) + var top_index: int = maxi(index - columns, 0) + var bottom_index: int = mini(index + columns, cards.size() - 1) + card.focus_neighbor_left = card.get_path_to(cards[left_index]) + card.focus_neighbor_right = card.get_path_to(cards[right_index]) + card.focus_neighbor_top = card.get_path_to(cards[top_index]) + card.focus_neighbor_bottom = card.get_path_to(cards[bottom_index]) + + func _compare_catches(left: FishCatchType, right: FishCatchType) -> bool: var comparison: int = 0 match _sort_mode: @@ -540,92 +995,23 @@ func _compare_catches(left: FishCatchType, right: FishCatchType) -> bool: func _create_inventory_card(fish_catch: FishCatchType) -> Button: - var card := Button.new() - card.theme_type_variation = &"CardButton" - card.custom_minimum_size = Vector2(132.0, 118.0) - card.toggle_mode = true + var card := BubbleInventoryCellScene.instantiate() as BubbleInventoryCellType + card.set_compact(_compact_layout) var is_selected: bool = _fish_selection.is_selected(fish_catch.catch_id) var is_focused: bool = ( fish_catch.catch_id == _fish_selection.get_focused_id() ) - card.button_pressed = is_selected - card.pressed.connect(_on_catch_card_pressed.bind(fish_catch.catch_id)) - _apply_inventory_card_styles( - card, - UIPalette.get_rarity_color(fish_catch.fish.rarity), - is_selected, - is_focused - ) - - var content := VBoxContainer.new() - content.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT, Control.PRESET_MODE_MINSIZE, 8) - content.add_theme_constant_override("separation", 3) - content.mouse_filter = Control.MOUSE_FILTER_IGNORE - card.add_child(content) - content.add_child(_create_texture_frame( + card.configure( fish_catch.fish.display_texture, - Vector2(108.0, 62.0) - )) - var name_label := Label.new() - name_label.text = fish_catch.fish.display_name - name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - name_label.mouse_filter = Control.MOUSE_FILTER_IGNORE - content.add_child(name_label) - var weight_label := Label.new() - weight_label.text = "%.2f lb" % fish_catch.weight_lb - weight_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - weight_label.add_theme_color_override( - "font_color", - UIPalette.MUTED_TEXT + fish_catch.fish.display_name, + "%.2f lb" % fish_catch.weight_lb, ) - weight_label.mouse_filter = Control.MOUSE_FILTER_IGNORE - content.add_child(weight_label) - if fish_catch.is_favorited: - var favorite_marker := Label.new() - favorite_marker.text = "★" - favorite_marker.add_theme_color_override( - "font_color", - UIPalette.SECONDARY - ) - favorite_marker.add_theme_font_size_override("font_size", 27) - favorite_marker.set_anchors_preset(Control.PRESET_TOP_RIGHT) - favorite_marker.offset_left = -28.0 - favorite_marker.offset_top = 5.0 - favorite_marker.offset_right = -8.0 - favorite_marker.offset_bottom = 29.0 - favorite_marker.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - favorite_marker.mouse_filter = Control.MOUSE_FILTER_IGNORE - card.add_child(favorite_marker) - if is_selected: - var selected_marker := Label.new() - selected_marker.text = "✓" - selected_marker.add_theme_color_override( - "font_color", - UIPalette.SUCCESS - ) - selected_marker.add_theme_font_size_override("font_size", 27) - selected_marker.set_anchors_preset(Control.PRESET_TOP_LEFT) - selected_marker.offset_left = 8.0 - selected_marker.offset_top = 5.0 - selected_marker.offset_right = 30.0 - selected_marker.offset_bottom = 29.0 - selected_marker.mouse_filter = Control.MOUSE_FILTER_IGNORE - card.add_child(selected_marker) - if is_focused: - var focus_marker := Label.new() - focus_marker.text = "◆" - focus_marker.add_theme_color_override( - "font_color", - UIPalette.PRIMARY - ) - focus_marker.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT) - focus_marker.offset_left = -26.0 - focus_marker.offset_top = -25.0 - focus_marker.offset_right = -8.0 - focus_marker.offset_bottom = -7.0 - focus_marker.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - focus_marker.mouse_filter = Control.MOUSE_FILTER_IGNORE - card.add_child(focus_marker) + card.set_item_state( + is_selected, + is_focused, + fish_catch.is_favorited, + ) + card.pressed.connect(_on_catch_card_pressed.bind(fish_catch.catch_id)) return card @@ -762,6 +1148,8 @@ func _update_sale_summary() -> void: ) if selected_count == 0: _selection_summary.text = "no fish selected" + _selection_status.set_content("selected", "none") + _offer_status.set_content("pelican offer", "—") _sell_button.text = "sell fish" _sell_button.disabled = true _sale_unavailable.text = "" @@ -778,6 +1166,7 @@ func _update_sale_summary() -> void: else "%d fish selected" % selected_count ) _selection_summary.text = count_text + _selection_status.set_content("selected", str(selected_count)) if ( preview != null and preview.payout >= 0 @@ -791,6 +1180,9 @@ func _update_sale_summary() -> void: _default_buyer.display_name, preview.payout, ] + _offer_status.set_content("pelican offer", "$%d" % preview.payout) + else: + _offer_status.set_content("pelican offer", "unavailable") _sell_button.disabled = preview == null or not preview.is_success() if preview != null and preview.status == FishSaleResultType.Status.FAVORITED: _sale_unavailable.text = ( diff --git a/ui/player_menu.tscn b/ui/player_menu.tscn index 824eda6..61a075e 100644 --- a/ui/player_menu.tscn +++ b/ui/player_menu.tscn @@ -1,7 +1,15 @@ -[gd_scene load_steps=3 format=3] +[gd_scene load_steps=9 format=3] [ext_resource type="Script" path="res://ui/player_menu.gd" id="1_menu"] [ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] +[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_content_shell.gd" id="3_shell"] +[ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="4_profile"] +[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_cluster.gd" id="5_cluster"] +[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_button.gd" id="6_bubble"] +[ext_resource type="PackedScene" path="res://ui/components/bubble_menu/bubble_status_bubble.tscn" id="7_status"] +[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_information_panel.gd" id="8_info"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_collection"] [node name="PlayerMenu" type="Control"] visible = false @@ -28,13 +36,16 @@ mouse_filter = 2 color = Color(0.015, 0.02, 0.03, 0.72) [node name="MenuPanel" type="PanelContainer" parent="."] -layout_mode = 1 -anchor_left = 0.02 -anchor_top = 0.025 -anchor_right = 0.98 -anchor_bottom = 0.96 -grow_horizontal = 2 -grow_vertical = 2 +unique_name_in_owner = true +layout_mode = 0 +offset_left = 42.0 +offset_top = 104.0 +offset_right = 1238.0 +offset_bottom = 582.0 +script = ExtResource("3_shell") +profile = ExtResource("4_profile") +content_margin = 22.0 +corner_radius = 74 [node name="Margin" type="MarginContainer" parent="MenuPanel"] layout_mode = 2 @@ -48,50 +59,19 @@ layout_mode = 2 theme_override_constants/separation = 8 [node name="Header" type="HBoxContainer" parent="MenuPanel/Margin/Layout"] +unique_name_in_owner = true layout_mode = 2 theme_override_constants/separation = 8 -[node name="Title" type="Label" parent="MenuPanel/Margin/Layout/Header"] -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1) -theme_override_font_sizes/font_size = 33 -text = "player menu" - [node name="WalletBalance" type="Label" parent="MenuPanel/Margin/Layout/Header"] unique_name_in_owner = true layout_mode = 2 +size_flags_horizontal = 3 text = "wallet: $0" theme_override_colors/font_color = Color(1, 0.82, 0.4, 1) -[node name="InventoryTab" type="Button" parent="MenuPanel/Margin/Layout/Header"] -unique_name_in_owner = true -layout_mode = 2 -toggle_mode = true -text = "cooler" -custom_minimum_size = Vector2(130, 58) - -[node name="BagTab" type="Button" parent="MenuPanel/Margin/Layout/Header"] -unique_name_in_owner = true -layout_mode = 2 -toggle_mode = true -text = "bag" -custom_minimum_size = Vector2(110, 58) - -[node name="LogbookTab" type="Button" parent="MenuPanel/Margin/Layout/Header"] -unique_name_in_owner = true -layout_mode = 2 -toggle_mode = true -text = "logbook" -custom_minimum_size = Vector2(140, 58) - -[node name="CloseButton" type="Button" parent="MenuPanel/Margin/Layout/Header"] -unique_name_in_owner = true -layout_mode = 2 -text = "close" -custom_minimum_size = Vector2(110, 58) - [node name="Separator" type="HSeparator" parent="MenuPanel/Margin/Layout"] +unique_name_in_owner = true layout_mode = 2 [node name="TransactionFeedback" type="Label" parent="MenuPanel/Margin/Layout"] @@ -103,10 +83,26 @@ theme_override_colors/font_color = Color(1, 0.82, 0.4, 1) theme_override_font_sizes/font_size = 21 [node name="Content" type="Control" parent="MenuPanel/Margin/Layout"] +unique_name_in_owner = true custom_minimum_size = Vector2(0, 260) layout_mode = 2 size_flags_vertical = 3 +[node name="CoolerScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +horizontal_scroll_mode = 0 + +[node name="CoolerHost" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content/CoolerScroll"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + [node name="InventorySection" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content"] unique_name_in_owner = true layout_mode = 1 @@ -117,7 +113,26 @@ grow_horizontal = 2 grow_vertical = 2 theme_override_constants/separation = 7 +[node name="StatusShoal" type="HBoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_constants/separation = 8 +alignment = 1 + +[node name="WalletStatus" parent="MenuPanel/Margin/Layout/Content/InventorySection/StatusShoal" instance=ExtResource("7_status")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="CapacityStatus" parent="MenuPanel/Margin/Layout/Content/InventorySection/StatusShoal" instance=ExtResource("7_status")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="HeldValueStatus" parent="MenuPanel/Margin/Layout/Content/InventorySection/StatusShoal" instance=ExtResource("7_status")] +unique_name_in_owner = true +layout_mode = 2 + [node name="SortBar" type="HBoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"] +unique_name_in_owner = true layout_mode = 2 theme_override_constants/separation = 6 @@ -128,13 +143,21 @@ text = "sort:" [node name="SortOption" type="OptionButton" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"] unique_name_in_owner = true layout_mode = 2 -custom_minimum_size = Vector2(190, 58) +custom_minimum_size = Vector2(136, 104) [node name="SortDirection" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"] unique_name_in_owner = true layout_mode = 2 -custom_minimum_size = Vector2(190, 58) +custom_minimum_size = Vector2(136, 104) text = "newest first" +script = ExtResource("6_bubble") +profile = ExtResource("4_profile") +neutral_size = Vector2(136, 104) +minimum_font_size = 15 +maximum_font_size = 20 +horizontal_amplitude = 0.0 +vertical_amplitude = 0.0 +deformation_amplitude = 0.0 [node name="SortSpacer" type="Control" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"] layout_mode = 2 @@ -142,25 +165,29 @@ size_flags_horizontal = 3 [node name="CoolerCount" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"] unique_name_in_owner = true +visible = false layout_mode = 2 text = "0 / 12" theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1) [node name="HeldValue" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"] unique_name_in_owner = true +visible = false layout_mode = 2 text = "held fish base value: $0" theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1) -[node name="InventoryBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"] +[node name="InventoryBody" type="BoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"] +unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 -split_offset = 510 theme_override_constants/separation = 8 [node name="InventoryList" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody"] +unique_name_in_owner = true custom_minimum_size = Vector2(350, 0) layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxEmpty_collection") [node name="InventoryListMargin" type="MarginContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList"] layout_mode = 2 @@ -180,6 +207,7 @@ text = "your cooler is empty." horizontal_alignment = 1 [node name="InventoryScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin/InventoryStack"] +unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 horizontal_scroll_mode = 0 @@ -193,8 +221,13 @@ theme_override_constants/v_separation = 7 columns = 3 [node name="DetailPanel" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody"] +unique_name_in_owner = true custom_minimum_size = Vector2(210, 0) layout_mode = 2 +script = ExtResource("8_info") +profile = ExtResource("4_profile") +content_margin = 12.0 +corner_radius = 96 [node name="DetailMargin" type="MarginContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel"] layout_mode = 2 @@ -221,33 +254,75 @@ layout_mode = 2 theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1) theme_override_font_sizes/font_size = 29 horizontal_alignment = 1 +theme_override_colors/font_color = Color(0.035, 0.145, 0.22, 1) [node name="DetailData" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"] unique_name_in_owner = true layout_mode = 2 autowrap_mode = 2 horizontal_alignment = 1 +theme_override_colors/font_color = Color(0.035, 0.145, 0.22, 1) [node name="SelectionSummary" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"] unique_name_in_owner = true +visible = false layout_mode = 2 text = "no fish selected" autowrap_mode = 2 horizontal_alignment = 1 theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1) -[node name="FavoriteButton" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"] +[node name="ActionRow" type="HBoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"] +layout_mode = 2 +theme_override_constants/separation = 8 +alignment = 1 + +[node name="FavoriteButton" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack/ActionRow"] unique_name_in_owner = true layout_mode = 2 disabled = true text = "favorite" +custom_minimum_size = Vector2(112, 104) +size_flags_horizontal = 4 +script = ExtResource("6_bubble") +profile = ExtResource("4_profile") +neutral_size = Vector2(112, 104) +minimum_font_size = 15 +maximum_font_size = 20 +horizontal_amplitude = 0.0 +vertical_amplitude = 0.0 +deformation_amplitude = 0.0 -[node name="SellButton" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"] +[node name="SellButton" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack/ActionRow"] unique_name_in_owner = true layout_mode = 2 disabled = true text = "sell" -theme_type_variation = &"DangerButton" +custom_minimum_size = Vector2(112, 104) +size_flags_horizontal = 4 +script = ExtResource("6_bubble") +profile = ExtResource("4_profile") +neutral_size = Vector2(112, 104) +minimum_font_size = 15 +maximum_font_size = 20 +horizontal_amplitude = 0.0 +vertical_amplitude = 0.0 +deformation_amplitude = 0.0 + +[node name="ContextStatus" type="HBoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"] +layout_mode = 2 +theme_override_constants/separation = 6 +alignment = 1 + +[node name="SelectionStatus" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack/ContextStatus" instance=ExtResource("7_status")] +unique_name_in_owner = true +custom_minimum_size = Vector2(108, 78) +layout_mode = 2 + +[node name="OfferStatus" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack/ContextStatus" instance=ExtResource("7_status")] +unique_name_in_owner = true +custom_minimum_size = Vector2(128, 78) +layout_mode = 2 [node name="SaleUnavailable" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"] unique_name_in_owner = true @@ -272,6 +347,7 @@ theme_override_constants/separation = 7 layout_mode = 2 text = "drag hotbar-compatible items onto a slot below. right-click a slot to clear it." horizontal_alignment = 1 +autowrap_mode = 2 [node name="BagBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/BagSection"] layout_mode = 2 @@ -280,6 +356,7 @@ split_offset = 540 theme_override_constants/separation = 8 [node name="BagList" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody"] +unique_name_in_owner = true custom_minimum_size = Vector2(360, 0) layout_mode = 2 @@ -314,6 +391,7 @@ theme_override_constants/v_separation = 7 columns = 3 [node name="BagDetail" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody"] +unique_name_in_owner = true custom_minimum_size = Vector2(220, 0) layout_mode = 2 @@ -429,3 +507,99 @@ unique_name_in_owner = true layout_mode = 2 text = "cancel" custom_minimum_size = Vector2(170, 64) + +[node name="NavigationCluster" type="Control" parent="."] +unique_name_in_owner = true +layout_mode = 0 +offset_left = 260.0 +offset_top = 14.0 +offset_right = 1020.0 +offset_bottom = 110.0 +mouse_filter = 1 +script = ExtResource("5_cluster") +profile = ExtResource("4_profile") +desktop_reference_size = Vector2(760, 96) +compact_reference_size = Vector2(610, 74) + +[node name="InventoryTab" type="Button" parent="NavigationCluster"] +unique_name_in_owner = true +custom_minimum_size = Vector2(148, 138) +layout_mode = 0 +toggle_mode = true +text = "cooler" +script = ExtResource("6_bubble") +profile = ExtResource("4_profile") +neutral_size = Vector2(148, 138) +desktop_anchor = Vector2(118, 68) +compact_anchor = Vector2(118, 68) +compact_minimum_size = Vector2(84, 78) +minimum_font_size = 16 +maximum_font_size = 27 +horizontal_amplitude = 1.2 +vertical_amplitude = 2.4 +motion_period = 5.6 +motion_phase = 0.35 +deformation_amplitude = 0.012 +deformation_period = 6.4 + +[node name="BagTab" type="Button" parent="NavigationCluster"] +unique_name_in_owner = true +custom_minimum_size = Vector2(122, 116) +layout_mode = 0 +toggle_mode = true +text = "bag" +script = ExtResource("6_bubble") +profile = ExtResource("4_profile") +neutral_size = Vector2(122, 116) +desktop_anchor = Vector2(300, 62) +compact_anchor = Vector2(300, 62) +compact_minimum_size = Vector2(70, 68) +minimum_font_size = 16 +maximum_font_size = 25 +horizontal_amplitude = 1.0 +vertical_amplitude = 2.0 +motion_period = 5.1 +motion_phase = 1.65 +deformation_amplitude = 0.011 +deformation_period = 6.0 + +[node name="LogbookTab" type="Button" parent="NavigationCluster"] +unique_name_in_owner = true +custom_minimum_size = Vector2(158, 146) +layout_mode = 0 +toggle_mode = true +text = "logbook" +script = ExtResource("6_bubble") +profile = ExtResource("4_profile") +neutral_size = Vector2(158, 146) +desktop_anchor = Vector2(478, 70) +compact_anchor = Vector2(478, 70) +compact_minimum_size = Vector2(88, 80) +minimum_font_size = 16 +maximum_font_size = 27 +horizontal_amplitude = 1.3 +vertical_amplitude = 2.5 +motion_period = 5.9 +motion_phase = 2.85 +deformation_amplitude = 0.012 +deformation_period = 6.7 + +[node name="CloseButton" type="Button" parent="NavigationCluster"] +unique_name_in_owner = true +custom_minimum_size = Vector2(112, 106) +layout_mode = 0 +text = "close" +script = ExtResource("6_bubble") +profile = ExtResource("4_profile") +neutral_size = Vector2(112, 106) +desktop_anchor = Vector2(652, 61) +compact_anchor = Vector2(652, 61) +compact_minimum_size = Vector2(66, 64) +minimum_font_size = 15 +maximum_font_size = 23 +horizontal_amplitude = 1.0 +vertical_amplitude = 1.9 +motion_period = 5.3 +motion_phase = 4.2 +deformation_amplitude = 0.01 +deformation_period = 6.2