diff --git a/art/patterns/pattern_bluedot.png b/art/patterns/pattern_bluedot.png deleted file mode 100644 index 0fab28f..0000000 Binary files a/art/patterns/pattern_bluedot.png and /dev/null differ diff --git a/art/patterns/pattern_tealdot.png b/art/patterns/pattern_tealdot.png new file mode 100644 index 0000000..9b901b6 Binary files /dev/null and b/art/patterns/pattern_tealdot.png differ diff --git a/art/patterns/pattern_bluedot.png.import b/art/patterns/pattern_tealdot.png.import similarity index 72% rename from art/patterns/pattern_bluedot.png.import rename to art/patterns/pattern_tealdot.png.import index c32a736..a6664e4 100644 --- a/art/patterns/pattern_bluedot.png.import +++ b/art/patterns/pattern_tealdot.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d0o3ym81t1sxs" -path="res://.godot/imported/pattern_bluedot.png-2880ee070284314ed1e7b46e1dbc0570.ctex" +uid="uid://f8a0cs0s7ler" +path="res://.godot/imported/pattern_tealdot.png-ea096873729758aad8d393553ab96b2d.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://art/patterns/pattern_bluedot.png" -dest_files=["res://.godot/imported/pattern_bluedot.png-2880ee070284314ed1e7b46e1dbc0570.ctex"] +source_file="res://art/patterns/pattern_tealdot.png" +dest_files=["res://.godot/imported/pattern_tealdot.png-ea096873729758aad8d393553ab96b2d.ctex"] [params] diff --git a/main/main.gd b/main/main.gd index bba114d..2564315 100644 --- a/main/main.gd +++ b/main/main.gd @@ -180,9 +180,7 @@ func _ready() -> void: ): _settings_manager.settings_changed.connect(_apply_runtime_settings) _settings_manager.load_settings() - _interface_fonts.set_readable_font_enabled( - _settings_manager.current_settings.use_readable_interface_font - ) + _interface_fonts.enforce_standard_font() if _data_root.resolve(): _configure_portable_stores() _initialize_after_data_root() @@ -856,9 +854,6 @@ func _process(_delta: float) -> void: func _apply_runtime_settings(settings: PlayerSettingsType) -> void: if settings == null: return - _interface_fonts.set_readable_font_enabled( - settings.use_readable_interface_font - ) _apply_world_pixelation(settings.world_pixel_size) _ui_pixelation.set_pixel_size(settings.ui_pixel_size) _game_ui.get_title_screen().set_world_pixelation( @@ -935,7 +930,7 @@ func _set_player_menu_backdrop_visible(is_visible: bool) -> void: _player_menu_backdrop, "modulate:a", 1.0, - UIMotion.UTILITY_ENTER_DURATION, + UIMotion.PLAYER_MENU_ENTER_DURATION, ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) return if not _player_menu_backdrop.visible: @@ -945,7 +940,7 @@ func _set_player_menu_backdrop_visible(is_visible: bool) -> void: _player_menu_backdrop, "modulate:a", 0.0, - UIMotion.UTILITY_EXIT_DURATION, + UIMotion.PLAYER_MENU_EXIT_DURATION, ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) _player_menu_backdrop_tween.finished.connect( func() -> void: diff --git a/main/main.tscn b/main/main.tscn index 3a100c7..2be860d 100644 --- a/main/main.tscn +++ b/main/main.tscn @@ -40,7 +40,7 @@ [ext_resource type="Script" path="res://network/identity_backup_service.gd" id="38_identity_backup"] [ext_resource type="Script" path="res://ui/interface_font_controller.gd" id="39_fonts"] [ext_resource type="Shader" path="res://ui/title_water_background.gdshader" id="40_title_water"] -[ext_resource type="Texture2D" path="res://art/patterns/pattern_bluedot.png" id="41_menu_pattern"] +[ext_resource type="Texture2D" path="res://art/patterns/pattern_tealdot.png" id="41_menu_pattern"] [ext_resource type="Shader" path="res://ui/player_menu_pattern.gdshader" id="42_menu_pattern"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water_native"] diff --git a/settings/player_settings.gd b/settings/player_settings.gd index 49b5e50..ba5b6ff 100644 --- a/settings/player_settings.gd +++ b/settings/player_settings.gd @@ -21,7 +21,8 @@ const UI_DESKTOP_RENDER_HEIGHTS: Array[int] = [0, 612, 504, 396, 288] const UI_COMPACT_RENDER_HEIGHTS: Array[int] = [0, 408, 336, 264, 192] @export var auto_click_enabled: bool = false -@export var use_readable_interface_font: bool = false +# Legacy settings-file field. Tuffy is now always used at runtime. +@export var use_readable_interface_font: bool = true @export_range(0.10, 0.50, 0.01) var auto_click_interval: float = 0.20 @export_range(0.001, 0.012, 0.0005) var mouse_camera_sensitivity: float = 0.005 @export_range(0.5, 5.0, 0.1) var controller_camera_sensitivity: float = 2.5 @@ -54,7 +55,7 @@ func is_valid() -> bool: func copy() -> PlayerSettings: var result := PlayerSettings.new() result.auto_click_enabled = auto_click_enabled - result.use_readable_interface_font = use_readable_interface_font + result.use_readable_interface_font = true result.auto_click_interval = auto_click_interval result.mouse_camera_sensitivity = mouse_camera_sensitivity result.controller_camera_sensitivity = controller_camera_sensitivity diff --git a/settings/player_settings_manager.gd b/settings/player_settings_manager.gd index 363c398..27e69a7 100644 --- a/settings/player_settings_manager.gd +++ b/settings/player_settings_manager.gd @@ -61,9 +61,8 @@ func load_settings() -> bool: return _use_defaults_after_corruption("Player settings values are invalid.") var loaded := PlayerSettings.new() loaded.auto_click_enabled = accessibility["auto_click_enabled"] - loaded.use_readable_interface_font = bool( - accessibility.get("use_readable_interface_font", false) - ) + # Accept the legacy field, but normalize runtime and future saved state. + loaded.use_readable_interface_font = true loaded.auto_click_interval = _read_float( accessibility.get("auto_click_interval"), -1.0 @@ -159,9 +158,7 @@ func save_now() -> bool: "accessibility": { "auto_click_enabled": current_settings.auto_click_enabled, "auto_click_interval": current_settings.auto_click_interval, - "use_readable_interface_font": ( - current_settings.use_readable_interface_font - ), + "use_readable_interface_font": true, }, "camera": { "mouse_sensitivity": current_settings.mouse_camera_sensitivity, diff --git a/ui/components/inventory_notepad.gd b/ui/components/inventory_notepad.gd index 6c6f00c..de346c6 100644 --- a/ui/components/inventory_notepad.gd +++ b/ui/components/inventory_notepad.gd @@ -10,6 +10,7 @@ const CONTENT_MARGIN_LEFT := 18.0 const CONTENT_MARGIN_TOP := 98.0 const CONTENT_MARGIN_RIGHT := 18.0 const CONTENT_MARGIN_BOTTOM := 16.0 +const HANDWRITTEN_FONT: Font = preload("res://ui/fonts/seattle_avenue.otf") @export var title_text := "notes" @export var inset_content := true @@ -17,14 +18,25 @@ const CONTENT_MARGIN_BOTTOM := 16.0 func _ready() -> void: mouse_filter = Control.MOUSE_FILTER_PASS - add_theme_font_override("font", UtilityPageStyle.TuffyFont) + apply_handwritten_to(self) add_theme_stylebox_override("panel", make_paper_style(inset_content)) resized.connect(queue_redraw) queue_redraw() +static func apply_handwritten_to(root: Control) -> void: + root.add_theme_font_override("font", HANDWRITTEN_FONT) + for descendant: Node in root.find_children("*", "Control", true, false): + var control := descendant as Control + control.add_theme_font_override( + "font", HANDWRITTEN_FONT + ) + if control is Label: + control.add_theme_color_override("font_color", INK_COLOR) + + func _draw() -> void: - var font: Font = UtilityPageStyle.TuffyFont + var font: Font = HANDWRITTEN_FONT draw_string( font, Vector2(28.0, 25.0), diff --git a/ui/components/organizer_tab.gd b/ui/components/organizer_tab.gd index 7f000e0..8db5122 100644 --- a/ui/components/organizer_tab.gd +++ b/ui/components/organizer_tab.gd @@ -1,27 +1,37 @@ class_name OrganizerTab extends Button -const INACTIVE_COLOR := Color("d6b875") -const SELECTED_COLOR := Color("f0d995") -const BORDER_COLOR := Color("4c3a25") -const TEXT_COLOR := Color("251b10") -const DISABLED_COLOR := Color("aa9a76") +const PALETTE: Array[Color] = [ + Color("b9deea"), + Color("3d9ca0"), + Color("4f9b72"), +] +const TEXT_COLOR := Color("102b35") const RISE_DURATION: float = UIMotion.UTILITY_ENTER_DURATION -const INACTIVE_SETTLE_Y: float = 8.0 -const SELECTED_SETTLE_Y: float = 0.0 -const HOVER_SETTLE_Y: float = 4.0 +const INACTIVE_SETTLE_Y: float = 7.0 +const SELECTED_SETTLE_Y: float = 1.0 +const HOVER_SETTLE_Y: float = 5.0 const HIDDEN_SETTLE_Y: float = 30.0 +const CAP_HEIGHT: float = 22.0 +const FONT_SIZE: int = 15 + +@export_range(0, 2, 1) var palette_index: int = 0: + set(value): + palette_index = clampi(value, 0, PALETTE.size() - 1) + queue_redraw() var _motion_tween: Tween var _hovered: bool = false var _motion_ready: bool = false var _visual_y: float = INACTIVE_SETTLE_Y +var _selected: bool = false func _ready() -> void: toggle_mode = true + _selected = button_pressed add_theme_font_override("font", UtilityPageStyle.TuffyFont) - add_theme_font_size_override("font_size", 18) + add_theme_font_size_override("font_size", FONT_SIZE) for color_name: StringName in [ &"font_color", &"font_hover_color", @@ -45,6 +55,12 @@ func refresh_state(animate: bool = true) -> void: _move_to_target(animate) +func set_selected(value: bool, animate: bool = true) -> void: + _selected = value + set_pressed_no_signal(value) + refresh_state(animate) + + func animate_entrance(delay: float = 0.0) -> void: _cancel_motion() _motion_ready = true @@ -77,7 +93,14 @@ func _initialize_motion() -> void: _set_visual_y(_target_y()) -func _on_toggled(_pressed: bool) -> void: +func _on_toggled(pressed: bool) -> void: + if _selected and not pressed: + # A selected organizer tab is a page marker, not a collapsible toggle. + # Restore without another signal before any lower-state frame is drawn. + set_pressed_no_signal(true) + refresh_state(false) + return + _selected = pressed refresh_state() @@ -107,35 +130,43 @@ func _move_to_target(animate: bool) -> void: func _draw() -> void: - var base_color: Color = SELECTED_COLOR if button_pressed else INACTIVE_COLOR - var border_width: int = 2 - var shadow_alpha: float = 0.28 - if disabled: - base_color = DISABLED_COLOR - border_width = 1 - shadow_alpha = 0.12 - elif has_focus(): - base_color = base_color.lightened(0.09) - border_width = 4 - shadow_alpha = 0.40 - elif _hovered: - base_color = base_color.lightened(0.07) - border_width = 3 - shadow_alpha = 0.34 - var visual_rect := Rect2(Vector2(0.0, _visual_y), size) - draw_style_box( - _make_style(base_color, border_width, shadow_alpha), - visual_rect, + var base_color: Color = PALETTE[palette_index] + base_color = ( + base_color.lightened(0.06) + if button_pressed + else base_color.darkened(0.07) ) + if disabled: + base_color = base_color.lerp(Color("8a978f"), 0.62) + elif has_focus(): + base_color = base_color.lightened(0.12) + elif _hovered: + base_color = base_color.lightened(0.08) + var width: float = size.x + var height: float = size.y + var points := PackedVector2Array([ + Vector2(10.0, _visual_y), + Vector2(width - 10.0, _visual_y), + Vector2(width - 5.0, _visual_y + 5.0), + Vector2(width - 5.0, _visual_y + 17.0), + Vector2(width, _visual_y + 26.0), + Vector2(width, _visual_y + height), + Vector2(0.0, _visual_y + height), + Vector2(0.0, _visual_y + 26.0), + Vector2(5.0, _visual_y + 17.0), + Vector2(5.0, _visual_y + 5.0), + ]) + draw_colored_polygon(points, base_color) var font: Font = UtilityPageStyle.TuffyFont var ink: Color = Color(TEXT_COLOR, 0.52) if disabled else TEXT_COLOR + var baseline: float = _visual_y + CAP_HEIGHT * 0.5 + FONT_SIZE * 0.35 draw_string( font, - Vector2(0.0, _visual_y + 32.0), + Vector2(0.0, baseline), text, HORIZONTAL_ALIGNMENT_CENTER, size.x, - 18, + FONT_SIZE, ink, ) @@ -156,28 +187,6 @@ func _set_visual_y(value: float) -> void: queue_redraw() -func _make_style(fill: Color, border_width: int, shadow_alpha: float) -> StyleBoxFlat: - var style := StyleBoxFlat.new() - style.bg_color = fill - style.border_color = BORDER_COLOR - style.border_width_left = border_width - style.border_width_top = border_width - style.border_width_right = border_width - style.border_width_bottom = 1 - style.corner_radius_top_left = 13 - style.corner_radius_top_right = 13 - style.corner_radius_bottom_left = 2 - style.corner_radius_bottom_right = 2 - style.shadow_color = Color(0.12, 0.08, 0.04, shadow_alpha) - style.shadow_size = 4 - style.shadow_offset = Vector2(0.0, 3.0) - style.content_margin_left = 14.0 - style.content_margin_right = 14.0 - style.content_margin_top = 8.0 - style.content_margin_bottom = 12.0 - return style - - func _cancel_motion() -> void: if _motion_tween != null: _motion_tween.kill() diff --git a/ui/game_theme.tres b/ui/game_theme.tres index a7d4282..13d5de5 100644 --- a/ui/game_theme.tres +++ b/ui/game_theme.tres @@ -1,6 +1,6 @@ [gd_resource type="Theme" load_steps=13 format=3] -[ext_resource type="FontFile" uid="uid://ddbejmxpa3kw6" path="res://ui/fonts/seattle_avenue.otf" id="1_font"] +[ext_resource type="FontFile" path="res://ui/fonts/Tuffy_Bold.otf" id="1_font"] [sub_resource type="StyleBoxFlat" id="Panel"] bg_color = Color(0.075, 0.145, 0.17, 0.96) diff --git a/ui/game_ui.gd b/ui/game_ui.gd index e58967a..d720b70 100644 --- a/ui/game_ui.gd +++ b/ui/game_ui.gd @@ -78,6 +78,9 @@ var _item_effects: PlayerItemEffectsType func _ready() -> void: + _hotbar_ui.presentation_transition_finished.connect( + _on_hotbar_presentation_transition_finished + ) _title_settings_panel.panel_visibility_changed.connect( _on_settings_visibility_changed ) @@ -284,6 +287,7 @@ func set_system_menu_open(is_open: bool) -> void: ) _hotbar_ui.set_drag_enabled(_player_menu_open and not is_open) if is_open: + _hotbar_ui.set_drag_enabled(false) _shop_prompt.hide() _emit_interactive_pointer_ui_changed() @@ -502,21 +506,27 @@ func _on_player_menu_visibility_changed(is_open: bool) -> void: _player_menu_open = is_open _gameplay_transient_hud.visible = _gameplay_ui_enabled and not is_open if is_open: + _hotbar_ui.set_drag_enabled(false) _hotbar_ui.set_presentation_visible(false, false) if _player_menu_hotbar_visible: _hotbar_ui.set_player_menu_context(true) - _hotbar_ui.set_presentation_visible(true, true) + _hotbar_ui.set_presentation_visible( + true, + true, + UIMotion.PLAYER_MENU_ENTER_DURATION, + ) + player_menu_backdrop_visibility_changed.emit(true) else: _player_menu_hotbar_visible = false _hotbar_ui.set_presentation_visible(false, false) _hotbar_ui.set_player_menu_context(false) - player_menu_backdrop_visibility_changed.emit(is_open) _refresh_chat_availability() _hotbar_ui.set_gameplay_input_enabled( _gameplay_ui_enabled and not is_open ) - _hotbar_ui.set_drag_enabled(is_open) - _refresh_hotbar_visibility() + if not is_open: + _hotbar_ui.set_drag_enabled(false) + _refresh_hotbar_visibility() _refresh_fishing_panel_visibility() _emit_interactive_pointer_ui_changed() if is_open: @@ -529,7 +539,12 @@ func _on_player_menu_exit_started() -> void: if not _player_menu_open: return _hotbar_ui.set_drag_enabled(false) - _hotbar_ui.set_presentation_visible(false, true) + _hotbar_ui.set_presentation_visible( + false, + true, + UIMotion.PLAYER_MENU_EXIT_DURATION, + ) + player_menu_backdrop_visibility_changed.emit(false) func _on_shop_visibility_changed(is_open: bool) -> void: @@ -550,13 +565,39 @@ func _on_shop_visibility_changed(is_open: bool) -> void: func _on_inventory_hotbar_context_changed(show_hotbar: bool) -> void: var context_changed: bool = _player_menu_hotbar_visible != show_hotbar _player_menu_hotbar_visible = show_hotbar - if _player_menu_open and show_hotbar and context_changed: - _hotbar_ui.set_presentation_visible(false, false) - _hotbar_ui.set_player_menu_context(true) - _hotbar_ui.set_drag_enabled(_player_menu_open and show_hotbar) + if _player_menu_open and context_changed: + _hotbar_ui.set_drag_enabled(false) + if show_hotbar: + _hotbar_ui.set_presentation_visible(false, false) + _hotbar_ui.set_player_menu_context(true) + _hotbar_ui.set_presentation_visible( + true, + true, + UIMotion.PLAYER_MENU_INVENTORY_DURATION, + ) + else: + _hotbar_ui.set_presentation_visible( + false, + true, + UIMotion.PLAYER_MENU_INVENTORY_DURATION, + ) + elif _player_menu_open and show_hotbar: + _hotbar_ui.set_drag_enabled(true) if not context_changed: return - _refresh_hotbar_visibility() + if not _player_menu_open: + _refresh_hotbar_visibility() + + +func _on_hotbar_presentation_transition_finished( + is_visible: bool, +) -> void: + _hotbar_ui.set_drag_enabled( + is_visible + and _player_menu_open + and _player_menu_hotbar_visible + and not _system_menu_open + ) func _refresh_hotbar_visibility() -> void: diff --git a/ui/hotbar.gd b/ui/hotbar.gd index c57f5ff..ca388a9 100644 --- a/ui/hotbar.gd +++ b/ui/hotbar.gd @@ -1,6 +1,8 @@ class_name HotbarUI extends Control +signal presentation_transition_finished(is_visible: bool) + const ItemCatalogType = preload("res://items/item_catalog.gd") const PlayerBagType = preload("res://inventory/player_bag.gd") const PlayerHotbarType = preload("res://inventory/player_hotbar.gd") @@ -14,6 +16,9 @@ const COMPACT_REFERENCE_SIZE := Vector2(640.0, 480.0) const HOTBAR_PRESENTATION_SCALE: float = 0.60 const HOTBAR_CANONICAL_POSITION := Vector2(256.0, 288.0) const HOTBAR_MENU_POSITION := Vector2(80.0, 198.0) +const HOTBAR_GAMEPLAY_Z_INDEX: int = 35 +# PlayerMenu is z=30 and its authored inventory panels are relative z=50. +const HOTBAR_MENU_Z_INDEX: int = 90 @onready var _presentation_scale_root: Control = %HotbarPresentationScaleRoot @onready var _bubble_field: Control = %BubbleField @@ -90,28 +95,49 @@ func set_player_menu_context(enabled: bool) -> void: if _player_menu_context == enabled: return _player_menu_context = enabled + z_index = HOTBAR_MENU_Z_INDEX if enabled else HOTBAR_GAMEPLAY_Z_INDEX _presentation_scale_root.position = ( HOTBAR_MENU_POSITION if enabled else HOTBAR_CANONICAL_POSITION ) -func set_presentation_visible(should_show: bool, animate: bool = true) -> void: +func set_presentation_visible( + should_show: bool, + animate: bool = true, + transition_duration: float = -1.0, +) -> void: _visibility_generation += 1 var generation: int = _visibility_generation if _visibility_tween != null: _visibility_tween.kill() _visibility_tween = null if should_show: + var was_visible: bool = visible visible = true if not animate: modulate.a = 1.0 + presentation_transition_finished.emit(true) return + if not was_visible: + modulate.a = 0.0 + var duration: float = ( + transition_duration + if transition_duration >= 0.0 + else UIMotion.UTILITY_ENTER_DURATION + ) _visibility_tween = create_tween() _visibility_tween.tween_property( self, "modulate:a", 1.0, - UIMotion.UTILITY_ENTER_DURATION, + duration, + ) + _visibility_tween.finished.connect( + func() -> void: + if generation != _visibility_generation: + return + _visibility_tween = null + presentation_transition_finished.emit(true) ) else: if not visible: @@ -119,13 +145,19 @@ func set_presentation_visible(should_show: bool, animate: bool = true) -> void: if not animate: visible = false modulate.a = 1.0 + presentation_transition_finished.emit(false) return + var duration: float = ( + transition_duration + if transition_duration >= 0.0 + else UIMotion.UTILITY_EXIT_DURATION + ) _visibility_tween = create_tween() _visibility_tween.tween_property( self, "modulate:a", 0.0, - UIMotion.UTILITY_EXIT_DURATION, + duration, ) _visibility_tween.finished.connect( func() -> void: @@ -134,6 +166,7 @@ func set_presentation_visible(should_show: bool, animate: bool = true) -> void: visible = false modulate.a = 1.0 _visibility_tween = null + presentation_transition_finished.emit(false) ) diff --git a/ui/interface_font_controller.gd b/ui/interface_font_controller.gd index 88562e0..239ebbd 100644 --- a/ui/interface_font_controller.gd +++ b/ui/interface_font_controller.gd @@ -1,40 +1,31 @@ class_name InterfaceFontController extends Node -signal font_mode_changed(use_readable_font: bool) - -const READABLE_FONT_PATH: String = "res://ui/fonts/Tuffy_Bold.otf" +const STANDARD_FONT: Font = preload("res://ui/fonts/Tuffy_Bold.otf") var _game_theme: Theme = preload("res://ui/game_theme.tres") -var _game_font: Font = preload("res://ui/fonts/seattle_avenue.otf") -var _readable_font: Font var _utility_theme: Theme -var _use_readable_font: bool = false func _ready() -> void: - if ResourceLoader.exists(READABLE_FONT_PATH): - _readable_font = load(READABLE_FONT_PATH) as Font - if _readable_font == null: - push_error("Readable interface font resource is unavailable.") - else: - _readable_font.fallbacks = [_game_font] + enforce_standard_font() _utility_theme = _game_theme.duplicate(true) - _utility_theme.default_font = readable_font() + _utility_theme.default_font = STANDARD_FONT -func set_readable_font_enabled(enabled: bool) -> void: - _use_readable_font = enabled - _game_theme.default_font = ( - _readable_font - if enabled and _readable_font != null - else _game_font - ) - font_mode_changed.emit(_use_readable_font) +func enforce_standard_font() -> void: + _game_theme.default_font = STANDARD_FONT + if _utility_theme != null: + _utility_theme.default_font = STANDARD_FONT + + +func set_readable_font_enabled(_enabled: bool) -> void: + # Compatibility seam for callers compiled against the old setting. + enforce_standard_font() func is_readable_font_enabled() -> bool: - return _use_readable_font + return true func apply_utility_theme(themed_node: Node) -> void: @@ -42,9 +33,7 @@ func apply_utility_theme(themed_node: Node) -> void: return if _utility_theme == null: _utility_theme = _game_theme.duplicate(true) - _utility_theme.default_font = ( - _readable_font if _readable_font != null else _game_font - ) + _utility_theme.default_font = STANDARD_FONT if themed_node is Control: (themed_node as Control).theme = _utility_theme elif themed_node is Window: @@ -52,4 +41,4 @@ func apply_utility_theme(themed_node: Node) -> void: func readable_font() -> Font: - return _readable_font if _readable_font != null else _game_font + return STANDARD_FONT diff --git a/ui/logbook_page.gd b/ui/logbook_page.gd index 9f02f15..4b29621 100644 --- a/ui/logbook_page.gd +++ b/ui/logbook_page.gd @@ -16,6 +16,7 @@ const INK := Color("251b10") const MUTED_INK := Color("6d5b45") const PAPER := Color("f2e6c9") const PAPER_DARK := Color("e8d7b4") +const LOGBOOK_TAB_LEFT_INSET: float = 34.0 var _collection_log: CollectionLogType var _inventory: FishInventoryType @@ -97,7 +98,7 @@ func set_interactive(value: bool) -> void: ) tab.mouse_filter = ( Control.MOUSE_FILTER_STOP - if _interactive + if _interactive and not tab.button_pressed else Control.MOUSE_FILTER_IGNORE ) for entry: Button in _entry_buttons.values(): @@ -139,11 +140,13 @@ func _build_interface() -> void: outer.add_child(stack) var tabs := HBoxContainer.new() - tabs.position = Vector2(0.0, 10.0) - tabs.size = Vector2(520.0, 58.0) + # The 26 px page corner plus 8 px clearance keeps the flange behind + # the straight portion of the spread. + tabs.position = Vector2(LOGBOOK_TAB_LEFT_INSET, 20.0) + tabs.size = Vector2(384.0, 38.0) tabs.z_index = 10 tabs.mouse_filter = Control.MOUSE_FILTER_PASS - tabs.add_theme_constant_override("separation", 8) + tabs.add_theme_constant_override("separation", 6) stack.add_child(tabs) for category: LogbookCatalog.Category in [ LogbookCatalog.Category.FRESH_WATER, @@ -151,10 +154,11 @@ func _build_interface() -> void: LogbookCatalog.Category.OTHER, ]: var tab := OrganizerTabType.new() - tab.custom_minimum_size = Vector2(168, 58) + tab.custom_minimum_size = Vector2(124, 38) tab.toggle_mode = true tab.text = LogbookCatalog.category_label(category) - tab.button_pressed = category == _category + tab.palette_index = int(category) + tab.set_selected(category == _category, false) tab.pressed.connect(_select_category.bind(category)) tabs.add_child(tab) _category_tabs.append(tab) @@ -365,8 +369,9 @@ func _select_category(category: LogbookCatalog.Category) -> void: _selected_id = StringName() _selected_entry_key = StringName() for index: int in _category_tabs.size(): - _category_tabs[index].button_pressed = index == int(_category) - (_category_tabs[index] as OrganizerTabType).refresh_state() + (_category_tabs[index] as OrganizerTabType).set_selected( + index == int(_category) + ) _begin_category_transition() diff --git a/ui/player_menu.gd b/ui/player_menu.gd index ebc6674..b641ba8 100644 --- a/ui/player_menu.gd +++ b/ui/player_menu.gd @@ -66,16 +66,14 @@ const LogbookEntryScene = preload( "res://ui/components/bubble_menu/logbook_entry.tscn" ) -const PAGE_OUT_DURATION: float = 0.34 -const PAGE_IN_DURATION: float = 0.42 const LOGBOOK_PAGE_DURATION: float = 0.18 -const TRANSITION_SAFE_MARGIN: float = 28.0 const DESKTOP_REFERENCE_SIZE := Vector2(1280.0, 720.0) const COMPACT_REFERENCE_SIZE := Vector2(640.0, 480.0) const COMPACT_HEIGHT_THRESHOLD: float = 560.0 const NAVIGATION_PRESENTATION_SCALE: float = 0.60 const NAVIGATION_CANONICAL_POSITION := Vector2(424.0, 44.0) const NAVIGATION_SELECTED_SCALE: float = 1.02 +const INVENTORY_TAB_LEFT_INSET: float = 96.0 const COOLER_RARITY_COMMON := Color("e8eef0") const COOLER_RARITY_UNCOMMON := Color("64c87c") const COOLER_RARITY_RARE := Color("6098dd") @@ -131,6 +129,8 @@ const INVENTORY_NOTEPAD_POSITION := Vector2( const INVENTORY_NOTEPAD_SIZE := Vector2(278.0, 484.0) const INVENTORY_HEADER_INSET := Vector2(24.0, 20.0) const INVENTORY_HEADER_HEIGHT := 40.0 +const INVENTORY_MAIN_CORNER_RADIUS: int = 58 +const INVENTORY_INNER_CORNER_RADIUS: int = 45 @onready var _navigation_cluster: BubbleClusterType = %NavigationCluster @onready var _presentation_scale_root: Control = %PlayerMenuPresentationScaleRoot @@ -318,7 +318,8 @@ var _mail_rest_position: Vector2 = Vector2.ZERO var _profile_rest_position: Vector2 = Vector2.ZERO var _page_outgoing_root: Control var _page_incoming_root: Control -var _page_hosts_shared: bool = false +var _page_outgoing_content_root: Control +var _inventory_transition_group: Control var _fish_nodes: Dictionary[StringName, CoolerFishSpriteType] = {} var _sorted_catches: Array[FishCatchType] = [] var _bag_item_nodes: Dictionary[StringName, BagItemSpriteType] = {} @@ -334,6 +335,9 @@ var _logbook_page_tween: Tween func _ready() -> void: + _configure_inventory_transition_group() + _inventory_sub_tabs.move_child(_tackle_sub_tab, 1) + _inventory_sub_tabs.move_child(_bag_sub_tab, 2) _inventory_tab.pressed.connect(_show_last_inventory_section) _cooler_sub_tab.pressed.connect(_show_section.bind(Section.COOLER)) _bag_sub_tab.pressed.connect(_show_section.bind(Section.BAG)) @@ -401,6 +405,39 @@ func _ready() -> void: set_process(false) +func _configure_inventory_transition_group() -> void: + # Keep the organizer row and the panel that masks its lower flange under one + # alpha owner. Top-level page crossfades can then never expose a complete tab. + var original_layer_index: int = mini( + _inventory_sub_tabs.get_index(), + mini( + _cooler_page.get_index(), + mini(_bag_page.get_index(), _tackle_box_page.get_index()), + ), + ) + _inventory_transition_group = Control.new() + _inventory_transition_group.name = "InventoryTransitionGroup" + _inventory_transition_group.set_anchors_and_offsets_preset( + Control.PRESET_FULL_RECT + ) + _inventory_transition_group.mouse_filter = Control.MOUSE_FILTER_IGNORE + _presentation_scale_root.add_child(_inventory_transition_group) + # Adding a runtime container normally places it after NavigationCluster in + # GUI picking order. Retain the pages' authored layer so their full-screen + # PASS roots cannot cover the top-level navigation hitboxes. + _presentation_scale_root.move_child( + _inventory_transition_group, + original_layer_index, + ) + for control: Control in [ + _inventory_sub_tabs, + _cooler_page, + _bag_page, + _tackle_box_page, + ]: + control.reparent(_inventory_transition_group, true) + + func _apply_mail_notification_style() -> void: var background := StyleBoxFlat.new() background.bg_color = Color(0.95, 0.88, 0.63, 0.98) @@ -795,9 +832,9 @@ func _show_section_immediate(section: Section) -> void: else: _players_page.deactivate() _inventory_tab.button_pressed = _is_inventory_section(section) - _cooler_sub_tab.button_pressed = section == Section.COOLER - _bag_sub_tab.button_pressed = section == Section.BAG - _tackle_sub_tab.button_pressed = section == Section.TACKLE_BOX + _cooler_sub_tab.set_selected(section == Section.COOLER) + _bag_sub_tab.set_selected(section == Section.BAG) + _tackle_sub_tab.set_selected(section == Section.TACKLE_BOX) _refresh_inventory_organizer_tabs() _logbook_tab.button_pressed = section == Section.LOGBOOK _mail_tab.button_pressed = section == Section.MAIL @@ -817,8 +854,8 @@ func _is_inventory_section(section: Section) -> bool: func _refresh_inventory_organizer_tabs() -> void: for tab: OrganizerTabType in [ _cooler_sub_tab, - _bag_sub_tab, _tackle_sub_tab, + _bag_sub_tab, ]: tab.refresh_state() @@ -828,8 +865,8 @@ func _animate_inventory_tab_entry() -> void: return var tabs: Array[OrganizerTabType] = [ _cooler_sub_tab, - _bag_sub_tab, _tackle_sub_tab, + _bag_sub_tab, ] for index: int in tabs.size(): tabs[index].animate_entrance(float(index) * 0.025) @@ -838,8 +875,8 @@ func _animate_inventory_tab_entry() -> void: func _settle_inventory_tabs_for_close() -> void: for tab: OrganizerTabType in [ _cooler_sub_tab, - _bag_sub_tab, _tackle_sub_tab, + _bag_sub_tab, ]: tab.settle_for_close() @@ -905,8 +942,8 @@ func _configure_navigation_focus() -> void: bubble.focus_neighbor_bottom = bubble.focus_neighbor_right var inventory_tabs: Array[Button] = [ _cooler_sub_tab, - _bag_sub_tab, _tackle_sub_tab, + _bag_sub_tab, ] for index: int in inventory_tabs.size(): var tab: Button = inventory_tabs[index] @@ -918,13 +955,23 @@ func _configure_navigation_focus() -> void: ) tab.focus_neighbor_top = tab.get_path_to(_inventory_tab) _inventory_tab.focus_neighbor_bottom = _inventory_tab.get_path_to( - inventory_tabs[int(_last_inventory_section)] + _inventory_tab_for_section(_last_inventory_section) ) _tackle_sub_tab.focus_neighbor_bottom = _tackle_sub_tab.get_path_to( _bait_filter ) +func _inventory_tab_for_section(section: Section) -> Button: + match section: + Section.COOLER: + return _cooler_sub_tab + Section.BAG: + return _bag_sub_tab + _: + return _tackle_sub_tab + + func _configure_sale_confirmation_focus() -> void: _confirm_sale_button.focus_neighbor_left = ( _confirm_sale_button.get_path_to(_cancel_sale_button) @@ -981,11 +1028,13 @@ func _apply_inventory_styles() -> void: ]: UtilityPageStyle.apply_button(button) for panel: PanelContainer in [_tackle_main_panel]: + var panel_style := UtilityPageStyle.panel_style() + panel_style.set_corner_radius_all(INVENTORY_MAIN_CORNER_RADIUS) panel.add_theme_stylebox_override( "panel", - UtilityPageStyle.panel_style(), + panel_style, ) - for label: Label in [_tackle_empty, _tackle_detail_text]: + for label: Label in [_tackle_empty]: label.add_theme_font_override("font", UtilityPageStyle.TuffyFont) label.add_theme_color_override( "font_color", @@ -1129,13 +1178,13 @@ func _apply_cooler_wall_styles() -> void: outer.bg_color = Color(0.76, 0.9, 0.96, 1.0) outer.border_color = Color(0.91, 0.98, 1.0, 1.0) outer.set_border_width_all(7) - outer.set_corner_radius_all(58) + outer.set_corner_radius_all(INVENTORY_MAIN_CORNER_RADIUS) _cooler_outer_wall.add_theme_stylebox_override("panel", outer) var inner := StyleBoxFlat.new() inner.bg_color = Color(0.018, 0.16, 0.25, 1.0) inner.border_color = Color(0.56, 0.78, 0.86, 1.0) inner.set_border_width_all(5) - inner.set_corner_radius_all(45) + inner.set_corner_radius_all(INVENTORY_INNER_CORNER_RADIUS) _cooler_inner_liner.add_theme_stylebox_override("panel", inner) var scroll_bar := _cooler_scroll.get_v_scroll_bar() var scroll_grabber := StyleBoxFlat.new() @@ -1155,6 +1204,7 @@ func _apply_cooler_notepad_style() -> void: "panel", InventoryNotepadType.make_paper_style(false), ) + InventoryNotepadType.apply_handwritten_to(_detail_constellation) func _apply_navigation_styles() -> void: @@ -1221,19 +1271,13 @@ func _apply_bag_styles() -> void: outer.bg_color = Color(0.39, 0.31, 0.2, 1.0) outer.border_color = Color(0.67, 0.54, 0.33, 1.0) outer.set_border_width_all(7) - outer.corner_radius_top_left = 88 - outer.corner_radius_top_right = 64 - outer.corner_radius_bottom_right = 94 - outer.corner_radius_bottom_left = 70 + outer.set_corner_radius_all(INVENTORY_MAIN_CORNER_RADIUS) _bag_outer_wall.add_theme_stylebox_override("panel", outer) var inner := StyleBoxFlat.new() inner.bg_color = Color(0.92, 0.85, 0.68, 1.0) inner.border_color = Color(0.75, 0.62, 0.38, 1.0) inner.set_border_width_all(4) - inner.corner_radius_top_left = 68 - inner.corner_radius_top_right = 48 - inner.corner_radius_bottom_right = 72 - inner.corner_radius_bottom_left = 52 + inner.set_corner_radius_all(INVENTORY_INNER_CORNER_RADIUS) _bag_inner_liner.add_theme_stylebox_override("panel", inner) func _apply_logbook_styles() -> void: var backing := StyleBoxFlat.new() @@ -1312,8 +1356,10 @@ func _update_shell_layout() -> void: _compact_layout = compact var reference_size := DESKTOP_REFERENCE_SIZE _presentation_scale_root.size = reference_size - _presentation_scale_root.scale = Vector2.ONE + if not _transitioning: + _presentation_scale_root.scale = Vector2.ONE _presentation_scale_root.position = Vector2.ZERO + _presentation_scale_root.pivot_offset = reference_size * 0.5 _content_shell.position = Vector2(14.0, 92.0) if compact else Vector2(42.0, 104.0) _content_shell.size = Vector2(612.0, 286.0) if compact else Vector2(1196.0, 478.0) _presentation_rest_position = _content_shell.position @@ -1329,6 +1375,12 @@ func _update_shell_layout() -> void: _cooler_page.size = reference_size _cooler_page.position = Vector2.ZERO _cooler_rest_position = Vector2.ZERO + # The Bag shell has the widest 88 px upper-left curve. Eight more pixels + # place every tab flange behind a straight section of the shared panel. + _inventory_sub_tabs.position = ( + INVENTORY_MAIN_POSITION + + Vector2(INVENTORY_TAB_LEFT_INSET, -30.0) + ) _layout_cooler_fish(false) _cooler_outer_wall.position = INVENTORY_MAIN_POSITION _cooler_outer_wall.size = INVENTORY_MAIN_SIZE @@ -1447,7 +1499,7 @@ func _update_shell_layout() -> void: ) _bag_filter_tabs.size = Vector2(320.0, INVENTORY_HEADER_HEIGHT) _bag_host.custom_minimum_size = ( - Vector2(520.0, 152.0) if compact else Vector2(796.0, 358.0) + Vector2(520.0, 152.0) if compact else Vector2(788.0, 358.0) ) _bag_scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED _bag_detail_constellation.position = INVENTORY_NOTEPAD_POSITION @@ -1585,80 +1637,30 @@ func _layout_cooler_selection_text(compact: bool) -> void: func _begin_menu_entry() -> void: _transitioning = true _set_shell_interactive(false) + _reset_page_transition_visuals() _animate_inventory_tab_entry() set_process(true) var generation: int = _transition_generation - var start_offset: float = ( - _presentation_scale_root.size.y + TRANSITION_SAFE_MARGIN + _presentation_scale_root.pivot_offset = ( + _presentation_scale_root.size * 0.5 ) - _content_shell.position = _presentation_rest_position + Vector2.DOWN * start_offset - _cooler_page.position = _cooler_rest_position + Vector2.DOWN * start_offset - _bag_page.position = _bag_rest_position + Vector2.DOWN * start_offset - _tackle_box_page.position = ( - _tackle_rest_position + Vector2.DOWN * start_offset + _presentation_scale_root.modulate.a = 0.0 + _presentation_scale_root.scale = ( + Vector2.ONE * UIMotion.PLAYER_MENU_ENTER_SCALE ) - _logbook_page.position = _logbook_rest_position + Vector2.DOWN * start_offset - _mail_page.position = _mail_rest_position + Vector2.DOWN * start_offset - _profile_page.position = _profile_rest_position + Vector2.DOWN * start_offset - _players_page.position = Vector2.DOWN * start_offset - var navigation_rest: Vector2 = _navigation_cluster.position - _navigation_cluster.position = navigation_rest + Vector2.DOWN * start_offset - var transition_duration := UIMotion.bubble_duration(start_offset) _presentation_tween = create_tween().set_parallel(true) _presentation_tween.tween_property( - _content_shell, - "position", - _presentation_rest_position, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _presentation_scale_root, + "modulate:a", + 1.0, + UIMotion.PLAYER_MENU_ENTER_DURATION, + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) _presentation_tween.tween_property( - _cooler_page, - "position", - _cooler_rest_position, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _bag_page, - "position", - _bag_rest_position, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _tackle_box_page, - "position", - _tackle_rest_position, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _logbook_page, - "position", - _logbook_rest_position, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _mail_page, - "position", - _mail_rest_position, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _profile_page, - "position", - _profile_rest_position, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _players_page, - "position", - Vector2.ZERO, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _navigation_cluster, - "position", - navigation_rest, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _presentation_scale_root, + "scale", + Vector2.ONE, + UIMotion.PLAYER_MENU_ENTER_DURATION, + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) _presentation_tween.chain().tween_callback( _finish_menu_entry.bind(generation) ) @@ -1682,71 +1684,24 @@ func _begin_menu_exit(reason: CloseReason, restore_controls: bool) -> void: _transitioning = true _set_shell_interactive(false) _settle_inventory_tabs_for_close() + if _current_section == Section.LOGBOOK: + _catalog_logbook.deactivate() 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 - var transition_duration := UIMotion.bubble_duration( - end_y - _navigation_cluster.position.y - ) _presentation_tween = create_tween().set_parallel(true) _presentation_tween.tween_property( - _content_shell, - "position:y", - end_y, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _presentation_scale_root, + "modulate:a", + 0.0, + UIMotion.PLAYER_MENU_EXIT_DURATION, + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) _presentation_tween.tween_property( - _cooler_page, - "position:y", - -_cooler_page.size.y - TRANSITION_SAFE_MARGIN, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _bag_page, - "position:y", - -_bag_page.size.y - TRANSITION_SAFE_MARGIN, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _tackle_box_page, - "position:y", - -_tackle_box_page.size.y - TRANSITION_SAFE_MARGIN, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _logbook_page, - "position:y", - -_logbook_page.size.y - TRANSITION_SAFE_MARGIN, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _mail_page, - "position:y", - -_mail_page.size.y - TRANSITION_SAFE_MARGIN, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _profile_page, - "position:y", - -_profile_page.size.y - TRANSITION_SAFE_MARGIN, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _presentation_tween.tween_property( - _players_page, - "position:y", - -_players_page.size.y - TRANSITION_SAFE_MARGIN, - transition_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, - transition_duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _presentation_scale_root, + "scale", + Vector2.ONE * UIMotion.PLAYER_MENU_EXIT_SCALE, + UIMotion.PLAYER_MENU_EXIT_DURATION, + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) _presentation_tween.chain().tween_callback( _finish_menu_exit.bind( generation, @@ -1781,6 +1736,8 @@ func _finish_close( _page_transitioning = false _bag_drag_active = false _reset_page_transition_visuals() + _presentation_scale_root.modulate.a = 1.0 + _presentation_scale_root.scale = Vector2.ONE visible = false set_process(false) get_viewport().gui_release_focus() @@ -1838,74 +1795,59 @@ func _begin_page_transition(section: Section) -> void: _cancel_page_tween() _page_transitioning = true _set_content_interactive(false) - if _is_inventory_section(_current_section) and not _is_inventory_section(section): + get_viewport().gui_release_focus() + var outgoing_section: Section = _current_section + var outgoing_inventory: bool = _is_inventory_section(outgoing_section) + var incoming_inventory: bool = _is_inventory_section(section) + if outgoing_inventory and not incoming_inventory: _settle_inventory_tabs_for_close() _set_navigation_target(section) var generation: int = _page_transition_generation - _page_outgoing_root = _get_section_root(_current_section) - _page_incoming_root = _get_section_root(section) - _page_hosts_shared = _page_outgoing_root == _page_incoming_root - var outgoing_rest: Vector2 = _get_section_rest_position(_current_section) + _page_outgoing_content_root = _get_section_root(outgoing_section) + _page_outgoing_root = ( + _inventory_transition_group + if outgoing_inventory and not incoming_inventory + else _page_outgoing_content_root + ) + _page_incoming_root = ( + _inventory_transition_group + if incoming_inventory and not outgoing_inventory + else _get_section_root(section) + ) + var outgoing_rest: Vector2 = _get_section_rest_position(outgoing_section) var incoming_rest: Vector2 = _get_section_rest_position(section) - if ( - _is_inventory_section(_current_section) - and _is_inventory_section(section) - ): - _show_section_immediate(section) - _page_outgoing_root.visible = true - _page_incoming_root.visible = true - _page_outgoing_root.position = outgoing_rest - _page_incoming_root.position = incoming_rest - _page_outgoing_root.modulate.a = 1.0 - _page_incoming_root.modulate.a = 0.0 - _page_tween = create_tween() - _page_tween.tween_property( - _page_outgoing_root, - "modulate:a", - 0.0, - UIMotion.UTILITY_ENTER_DURATION, - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _page_tween.parallel().tween_property( - _page_incoming_root, - "modulate:a", - 1.0, - UIMotion.UTILITY_ENTER_DURATION, - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _page_tween.finished.connect( - _finish_page_transition.bind(generation), - CONNECT_ONE_SHOT, - ) - return - if not _page_hosts_shared: - _page_incoming_root.position = incoming_rest + Vector2.DOWN * 18.0 - _page_incoming_root.modulate.a = 0.0 - _page_tween = create_tween() + _show_section_immediate(section) + if outgoing_inventory and not incoming_inventory: + # Immediate page selection hides Inventory. Restore the outgoing content + # beneath its still-opaque masking group for the coordinated fade-out. + _page_outgoing_content_root.visible = true + _inventory_sub_tabs.visible = true + _inventory_transition_group.visible = true + _page_outgoing_root.visible = true + _page_incoming_root.visible = true + _page_outgoing_root.position = outgoing_rest + _page_incoming_root.position = incoming_rest + _page_outgoing_root.modulate.a = 1.0 + _page_incoming_root.modulate.a = 0.0 + if incoming_inventory and not outgoing_inventory: + _animate_inventory_tab_entry() + var duration: float = ( + UIMotion.PLAYER_MENU_INVENTORY_DURATION + if outgoing_inventory and incoming_inventory + else UIMotion.PLAYER_MENU_PAGE_DURATION + ) + _page_tween = create_tween().set_parallel(true) _page_tween.tween_property( _page_outgoing_root, "modulate:a", 0.0, - PAGE_OUT_DURATION + duration, ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _page_tween.parallel().tween_property( - _page_outgoing_root, - "position:y", - outgoing_rest.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( _page_incoming_root, "modulate:a", 1.0, - PAGE_IN_DURATION - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) - _page_tween.parallel().tween_property( - _page_incoming_root, - "position", - incoming_rest, - PAGE_IN_DURATION + duration, ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) _page_tween.finished.connect( _finish_page_transition.bind(generation), @@ -1913,19 +1855,6 @@ func _begin_page_transition(section: Section) -> void: ) -func _swap_page(section: Section, generation: int) -> void: - if generation != _page_transition_generation or not visible: - return - _show_section_immediate(section) - if _is_inventory_section(section): - _animate_inventory_tab_entry() - if _page_hosts_shared: - _page_incoming_root.position = ( - _presentation_rest_position + Vector2.DOWN * 18.0 - ) - _page_incoming_root.modulate.a = 0.0 - - func _finish_page_transition(generation: int) -> void: if generation != _page_transition_generation or not visible: return @@ -1934,8 +1863,16 @@ func _finish_page_transition(generation: int) -> void: if ( _page_outgoing_root != null and _page_outgoing_root != _page_incoming_root + and _page_outgoing_root != _inventory_transition_group ): _page_outgoing_root.visible = false + if ( + _page_outgoing_content_root != null + and not _is_inventory_section(_current_section) + ): + _page_outgoing_content_root.visible = false + _inventory_sub_tabs.visible = _is_inventory_section(_current_section) + _inventory_sub_tabs.modulate.a = 1.0 _reset_page_transition_visuals() _set_content_interactive(true) _focus_current_section() @@ -1991,7 +1928,7 @@ func _set_content_interactive(interactive: bool) -> void: ) tab.mouse_filter = ( Control.MOUSE_FILTER_STOP - if tab_interactive + if tab_interactive and not tab.button_pressed else Control.MOUSE_FILTER_IGNORE ) _logbook_page.mouse_filter = ( @@ -2084,8 +2021,13 @@ func _reset_page_transition_visuals() -> void: var page: Control = _get_section_root(section) page.position = _get_section_rest_position(section) page.modulate.a = 1.0 + if _inventory_transition_group != null: + _inventory_transition_group.modulate.a = 1.0 + _inventory_transition_group.visible = true + _inventory_sub_tabs.modulate.a = 1.0 _page_outgoing_root = null _page_incoming_root = null + _page_outgoing_content_root = null func _on_sort_selected(index: int, source: OptionButton) -> void: diff --git a/ui/player_menu.tscn b/ui/player_menu.tscn index 7de60dd..a26ae04 100644 --- a/ui/player_menu.tscn +++ b/ui/player_menu.tscn @@ -68,16 +68,16 @@ unique_name_in_owner = true visible = false z_index = 40 layout_mode = 0 -offset_left = 54.0 -offset_top = 126.0 -offset_right = 504.0 -offset_bottom = 184.0 +offset_left = 150.0 +offset_top = 136.0 +offset_right = 534.0 +offset_bottom = 174.0 mouse_filter = 1 -theme_override_constants/separation = 8 +theme_override_constants/separation = 6 [node name="CoolerSubTab" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/InventorySubTabs"] unique_name_in_owner = true -custom_minimum_size = Vector2(132, 58) +custom_minimum_size = Vector2(124, 38) layout_mode = 2 toggle_mode = true button_group = null @@ -86,21 +86,23 @@ script = ExtResource("23_organizer_tab") [node name="BagSubTab" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/InventorySubTabs"] unique_name_in_owner = true -custom_minimum_size = Vector2(132, 58) +custom_minimum_size = Vector2(124, 38) layout_mode = 2 toggle_mode = true button_group = null -text = "Bag" +text = "Equipment" script = ExtResource("23_organizer_tab") +palette_index = 2 [node name="TackleSubTab" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/InventorySubTabs"] unique_name_in_owner = true -custom_minimum_size = Vector2(154, 58) +custom_minimum_size = Vector2(124, 38) layout_mode = 2 toggle_mode = true button_group = null -text = "Tackle Box" +text = "Tackle" script = ExtResource("23_organizer_tab") +palette_index = 1 [node name="CoolerPage" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"] unique_name_in_owner = true @@ -541,7 +543,7 @@ horizontal_scroll_mode = 0 [node name="BagHost" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/BagPage/BagOuterWall/BagOuterMargin/BagInnerLiner/BagInnerMargin/BagScroll"] unique_name_in_owner = true -custom_minimum_size = Vector2(796, 358) +custom_minimum_size = Vector2(788, 358) layout_mode = 2 mouse_filter = 1 diff --git a/ui/settings_panel.gd b/ui/settings_panel.gd index a8faa93..8ff79c3 100644 --- a/ui/settings_panel.gd +++ b/ui/settings_panel.gd @@ -47,7 +47,6 @@ enum PresentationMode { @onready var _invert_y_toggle: BubbleButton = %InvertYToggle @onready var _auto_click_toggle: BubbleButton = %AutoClickToggle @onready var _auto_click_interval: BubbleButton = %AutoClickIntervalValue -@onready var _readable_font_toggle: BubbleButton = %ReadableFontToggle @onready var _world_options: Array[BubbleButton] = [ %WorldLegible, @@ -149,7 +148,6 @@ func _ready() -> void: ) _invert_y_toggle.pressed.connect(_toggle_invert_y) _auto_click_toggle.pressed.connect(_toggle_auto_click) - _readable_font_toggle.pressed.connect(_toggle_readable_font) for control: Control in [_auto_click_toggle, _auto_click_interval]: control.mouse_entered.connect( _show_accessibility_helper.bind(&"auto_click") @@ -157,12 +155,6 @@ func _ready() -> void: control.focus_entered.connect( _show_accessibility_helper.bind(&"auto_click") ) - _readable_font_toggle.mouse_entered.connect( - _show_accessibility_helper.bind(&"alternate_font") - ) - _readable_font_toggle.focus_entered.connect( - _show_accessibility_helper.bind(&"alternate_font") - ) %IntervalDecrease.pressed.connect(_adjust_auto_click_interval.bind(-1)) %IntervalIncrease.pressed.connect(_adjust_auto_click_interval.bind(1)) _auto_click_interval.pressed.connect( @@ -685,9 +677,6 @@ func _load_controls() -> void: _mouse_sensitivity = settings.mouse_camera_sensitivity _controller_sensitivity = settings.controller_camera_sensitivity _invert_camera_y = settings.invert_camera_y - _readable_font_toggle.button_pressed = ( - settings.use_readable_interface_font - ) _refresh_value_labels() @@ -715,14 +704,6 @@ func _refresh_value_labels() -> void: "accessibility\nauto-click\n" + ("on" if _auto_click_enabled else "off") ) - _readable_font_toggle.text = ( - "alternate\nfont\n" - + ( - "on" - if settings.use_readable_interface_font - else "off" - ) - ) _auto_click_interval.text = ( "auto-click\ninterval\n%.2f s" % _auto_click_interval_value ) @@ -734,16 +715,11 @@ func _refresh_value_labels() -> void: _ui_options[index].button_pressed = index + 1 == settings.ui_pixel_size -func _show_accessibility_helper(kind: StringName) -> void: +func _show_accessibility_helper(_kind: StringName) -> void: var helper := %IntervalHelp as BubbleButton - if kind == &"alternate_font": - helper.text = ( - "The quick brown fox\njumps over the lazy dog.\n0123456789" - ) - else: - helper.text = ( - "Lower intervals click\nbarriers faster\nwhile held." - ) + helper.text = ( + "Lower intervals click\nbarriers faster\nwhile held." + ) func _style_data_page() -> void: @@ -775,19 +751,6 @@ func _set_world_pixelation(pixel_size: int) -> void: _refresh_value_labels() -func _toggle_readable_font() -> void: - if _settings_manager == null: - return - var edited: PlayerSettings = _settings_manager.current_settings.copy() - edited.use_readable_interface_font = ( - not edited.use_readable_interface_font - ) - if not _settings_manager.apply_settings(edited): - _feedback.text = "failed to save accessibility settings." - return - _load_controls() - - func _set_ui_pixelation(pixel_size: int) -> void: if _settings_manager == null: return diff --git a/ui/settings_panel.tscn b/ui/settings_panel.tscn index 6bbd8a9..ad84849 100644 --- a/ui/settings_panel.tscn +++ b/ui/settings_panel.tscn @@ -486,8 +486,8 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("3_page") page_id = &"accessibility" -bubble_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/ReadableFontToggle"), NodePath("BubbleCluster/IntervalHelp"), NodePath("BubbleCluster/AccessibilityBackButton")]) -focus_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/ReadableFontToggle"), NodePath("BubbleCluster/AccessibilityBackButton")]) +bubble_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/IntervalHelp"), NodePath("BubbleCluster/AccessibilityBackButton")]) +focus_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/AccessibilityBackButton")]) initial_focus_path = NodePath("BubbleCluster/AutoClickToggle") back_focus_path = NodePath("BubbleCluster/AccessibilityBackButton") @@ -562,43 +562,6 @@ minimum_font_size = 13 maximum_font_size = 22 motion_phase = 4.1 -[node name="ReadableFontToggle" parent="AccessibilityPage/BubbleCluster" instance=ExtResource("4_bubble")] -unique_name_in_owner = true -custom_minimum_size = Vector2(0, 0) -toggle_mode = true -text = "alternate\nfont\noff" -neutral_size = Vector2(150, 150) -desktop_anchor = Vector2(92, 405) -compact_anchor = Vector2(102, 374) -compact_minimum_size = Vector2(156, 118) -minimum_font_size = 13 -maximum_font_size = 21 -motion_phase = 4.6 - -[node name="ReadableFontSample" type="Label" parent="AccessibilityPage/BubbleCluster"] -visible = false -layout_mode = 0 -offset_left = 250.0 -offset_top = 20.0 -offset_right = 700.0 -offset_bottom = 82.0 -mouse_filter = 2 -text = "The quick brown fox jumps over the lazy dog.\n0123456789" -horizontal_alignment = 1 -vertical_alignment = 1 -autowrap_mode = 2 - -[node name="ReadableFontHelper" type="Label" parent="AccessibilityPage/BubbleCluster"] -visible = false -layout_mode = 0 -offset_left = 250.0 -offset_top = 82.0 -offset_right = 700.0 -offset_bottom = 112.0 -mouse_filter = 2 -text = "Uses a clearer font for menus and interface text." -horizontal_alignment = 1 - [node name="AccessibilityBackButton" parent="AccessibilityPage/BubbleCluster" instance=ExtResource("4_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) diff --git a/ui/ui_motion.gd b/ui/ui_motion.gd index 55956ca..83ac66a 100644 --- a/ui/ui_motion.gd +++ b/ui/ui_motion.gd @@ -6,6 +6,12 @@ const BUBBLE_TRANSITION_OVERLAP_DELAY: float = 0.05 const UTILITY_ENTER_DURATION: float = 0.19 const UTILITY_EXIT_DURATION: float = 0.15 const UTILITY_ENTER_SCALE: float = 0.97 +const PLAYER_MENU_ENTER_DURATION: float = 0.16 +const PLAYER_MENU_EXIT_DURATION: float = 0.14 +const PLAYER_MENU_PAGE_DURATION: float = 0.12 +const PLAYER_MENU_INVENTORY_DURATION: float = 0.10 +const PLAYER_MENU_ENTER_SCALE: float = 0.985 +const PLAYER_MENU_EXIT_SCALE: float = 0.99 static func bubble_duration(distance: float) -> float: