diff --git a/project.godot b/project.godot index be0f21e..807e880 100644 --- a/project.godot +++ b/project.godot @@ -31,6 +31,7 @@ enabled=PackedStringArray("res://addons/netfishing_shoreline_baker/plugin.cfg") [gui] theme/custom="res://ui/game_theme.tres" +timers/tooltip_delay_sec=0.0 [input] diff --git a/ui/components/bubble_menu/bubble_confirmation_page.gd b/ui/components/bubble_menu/bubble_confirmation_page.gd index 0565406..d23b848 100644 --- a/ui/components/bubble_menu/bubble_confirmation_page.gd +++ b/ui/components/bubble_menu/bubble_confirmation_page.gd @@ -1,6 +1,8 @@ class_name BubbleConfirmationPage extends Control +const QUICK_ENTER_SCALE: float = 0.97 + signal confirmed signal cancelled @@ -23,8 +25,6 @@ enum InitialFocus { ^"BubbleCluster/CancelButton/CancelLabel" ) @export var maximum_layout_size: Vector2 = Vector2(720.0, 520.0) -@export_range(0.0, 128.0, 1.0) var transition_safe_margin: float = 24.0 - var _cluster: BubbleCluster var _message: BubbleButton var _message_label: Label @@ -50,6 +50,7 @@ func _ready() -> void: _cancel_label = get_node(cancel_label_path) as Label _bubbles = [_message, _confirm_button, _cancel_button] _cluster.configure(_bubbles) + _cluster.motion_scale = 0.0 _message.focus_mode = Control.FOCUS_NONE _message.mouse_filter = Control.MOUSE_FILTER_IGNORE _confirm_button.pressed.connect(confirmed.emit) @@ -79,18 +80,19 @@ func transition_in(duration: float, completed: Callable) -> void: _is_transitioning = true _set_interactive(false) set_process(true) - _cluster.position = Vector2( - _resting_cluster_position.x, - size.y + transition_safe_margin - ) + _cluster.position = _resting_cluster_position + _cluster.pivot_offset = _cluster.size * 0.5 + _cluster.scale = Vector2.ONE * QUICK_ENTER_SCALE + _cluster.modulate.a = 0.0 var generation: int = _transition_generation - _transition = create_tween() + _transition = create_tween().set_parallel(true) _transition.tween_property( _cluster, - "position", - _resting_cluster_position, + "scale", + Vector2.ONE, duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) + _transition.tween_property(_cluster, "modulate:a", 1.0, duration) _transition.finished.connect( _finish_transition_in.bind(generation, completed), CONNECT_ONE_SHOT @@ -104,14 +106,17 @@ func transition_out(duration: float, completed: Callable) -> void: _cancel_transition() _is_transitioning = true _set_interactive(false) + _cluster.position = _resting_cluster_position + _cluster.pivot_offset = _cluster.size * 0.5 var generation: int = _transition_generation - _transition = create_tween() + _transition = create_tween().set_parallel(true) _transition.tween_property( _cluster, - "position:y", - -_cluster.size.y - transition_safe_margin, + "scale", + Vector2.ONE * QUICK_ENTER_SCALE, duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + _transition.tween_property(_cluster, "modulate:a", 0.0, duration) _transition.finished.connect( _finish_transition_out.bind(generation, completed), CONNECT_ONE_SHOT @@ -127,6 +132,7 @@ func hide_page() -> void: hide() if _cluster != null: _cluster.position = _resting_cluster_position + _reset_cluster_presentation() func lock_interaction() -> void: @@ -165,6 +171,7 @@ func _update_layout() -> void: if not _is_transitioning: _cluster.position = _resting_cluster_position _cluster.size = field_size + _cluster.pivot_offset = _cluster.size * 0.5 _cluster.apply_layout(field_size, false) @@ -219,6 +226,7 @@ func _finish_transition_in( _transition = null _is_transitioning = false _cluster.position = _resting_cluster_position + _reset_cluster_presentation() _set_interactive(true) focus_initial() completed.call() @@ -234,6 +242,7 @@ func _finish_transition_out( _is_transitioning = false hide() _cluster.position = _resting_cluster_position + _reset_cluster_presentation() set_process(false) completed.call() @@ -242,3 +251,10 @@ func _cancel_transition() -> void: if _transition != null: _transition.kill() _transition = null + + +func _reset_cluster_presentation() -> void: + if _cluster == null: + return + _cluster.scale = Vector2.ONE + _cluster.modulate.a = 1.0 diff --git a/ui/pause_menu.gd b/ui/pause_menu.gd index 842382f..f07655b 100644 --- a/ui/pause_menu.gd +++ b/ui/pause_menu.gd @@ -3,7 +3,7 @@ extends Control const INPUT_OWNER: StringName = &"game_menu" const PAUSE_DESKTOP_REFERENCE_SIZE: Vector2 = Vector2(1280.0, 720.0) -const VISIBILITY_FADE_DURATION: float = 0.55 +const VISIBILITY_FADE_DURATION: float = 0.10 const PlayerType = preload("res://player/player.gd") const SaveManagerType = preload("res://save/player_save_manager.gd") const SettingsManagerType = preload( @@ -371,7 +371,7 @@ func _open_confirmation( func _show_confirmation() -> void: _confirmation_page.transition_in( - 0.0, + VISIBILITY_FADE_DURATION, _finish_confirmation_open ) @@ -386,7 +386,7 @@ func _close_confirmation() -> void: _confirmation_action = ConfirmationAction.NONE _emit_transition_flurry() _confirmation_page.transition_out( - 0.0, + VISIBILITY_FADE_DURATION, _finish_confirmation_cancel ) @@ -548,7 +548,7 @@ func _finish_close(reason: CloseReason, restore_controls: bool) -> void: func _emit_transition_flurry() -> void: - _transition_flurry.emit_flurry() + _transition_flurry.clear_flurries() func _update_responsive_pause_stage() -> void: diff --git a/ui/settings/settings_bubble_page.gd b/ui/settings/settings_bubble_page.gd index 2a8dc6d..3f533c9 100644 --- a/ui/settings/settings_bubble_page.gd +++ b/ui/settings/settings_bubble_page.gd @@ -4,6 +4,9 @@ extends Control const ControllerFocusNavigationType = preload( "res://ui/controller_focus_navigation.gd" ) +const QUICK_ENTER_DURATION: float = 0.14 +const QUICK_EXIT_DURATION: float = 0.10 +const QUICK_ENTER_SCALE: float = 0.97 @export var page_id: StringName @export var cluster_path: NodePath = ^"BubbleCluster" @@ -13,8 +16,6 @@ const ControllerFocusNavigationType = preload( @export var back_focus_path: NodePath @export var back_entry_path: NodePath @export var maximum_layout_size: Vector2 = Vector2(720.0, 520.0) -@export_range(0.0, 128.0, 1.0) var transition_safe_margin: float = 24.0 - var _cluster: BubbleCluster var _bubbles: Array[BubbleButton] = [] var _focus_controls: Array[Control] = [] @@ -35,6 +36,7 @@ func _ready() -> void: if focus_control != null: _focus_controls.append(focus_control) _cluster.configure(_bubbles) + _cluster.motion_scale = 0.0 _configure_focus_order() resized.connect(_update_layout) call_deferred("_update_layout") @@ -47,6 +49,7 @@ func show_page(should_focus: bool = true) -> void: show() _is_transitioning = false _cluster.position = _resting_cluster_position + _reset_cluster_presentation() set_process(true) _set_interactive(true) var paper := get_node_or_null("Paper") as Control @@ -68,18 +71,22 @@ func transition_out( if paper != null: UtilityPageStyle.animate_out(paper, Callable()) set_process(true) - var target_y: float = -_cluster.size.y - transition_safe_margin - var duration := UIMotion.bubble_duration( - target_y - _cluster.position.y - ) var generation: int = _transition_generation - _transition = create_tween() + _cluster.position = _resting_cluster_position + _cluster.pivot_offset = _cluster.size * 0.5 + _transition = create_tween().set_parallel(true) _transition.tween_property( _cluster, - "position:y", - target_y, - duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + "scale", + Vector2.ONE * QUICK_ENTER_SCALE, + QUICK_EXIT_DURATION + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + _transition.tween_property( + _cluster, + "modulate:a", + 0.0, + QUICK_EXIT_DURATION + ) _transition.finished.connect( _finish_transition_out.bind(generation, completed), CONNECT_ONE_SHOT @@ -97,24 +104,27 @@ func transition_in( _is_transitioning = true _set_interactive(false) set_process(true) - _cluster.position = Vector2( - _resting_cluster_position.x, - size.y + transition_safe_margin - ) + _cluster.position = _resting_cluster_position + _cluster.pivot_offset = _cluster.size * 0.5 + _cluster.scale = Vector2.ONE * QUICK_ENTER_SCALE + _cluster.modulate.a = 0.0 var paper := get_node_or_null("Paper") as Control if paper != null: UtilityPageStyle.animate_in(paper) - var duration := UIMotion.bubble_duration( - _cluster.position.y - _resting_cluster_position.y - ) var generation: int = _transition_generation - _transition = create_tween() + _transition = create_tween().set_parallel(true) _transition.tween_property( _cluster, - "position", - _resting_cluster_position, - duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + "scale", + Vector2.ONE, + QUICK_ENTER_DURATION + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) + _transition.tween_property( + _cluster, + "modulate:a", + 1.0, + QUICK_ENTER_DURATION + ) _transition.finished.connect( _finish_transition_in.bind(generation, should_focus, completed), CONNECT_ONE_SHOT @@ -130,6 +140,7 @@ func hide_page() -> void: hide() if _cluster != null: _cluster.position = _resting_cluster_position + _reset_cluster_presentation() func focus_initial() -> void: @@ -183,6 +194,7 @@ func _update_layout() -> void: if not _is_transitioning: _cluster.position = _resting_cluster_position _cluster.size = field_size + _cluster.pivot_offset = _cluster.size * 0.5 _cluster.apply_layout(field_size, false) _configure_focus_navigation() @@ -243,6 +255,7 @@ func _finish_transition_out(generation: int, completed: Callable) -> void: _is_transitioning = false hide() _cluster.position = _resting_cluster_position + _reset_cluster_presentation() if completed.is_valid(): completed.call() @@ -257,6 +270,7 @@ func _finish_transition_in( _transition = null _is_transitioning = false _cluster.position = _resting_cluster_position + _reset_cluster_presentation() set_process(true) _set_interactive(true) if should_focus: @@ -268,3 +282,10 @@ func _cancel_transition() -> void: if _transition != null: _transition.kill() _transition = null + + +func _reset_cluster_presentation() -> void: + if _cluster == null: + return + _cluster.scale = Vector2.ONE + _cluster.modulate.a = 1.0 diff --git a/ui/title_confirmation_bubble_page.gd b/ui/title_confirmation_bubble_page.gd index b4c3643..ac1d235 100644 --- a/ui/title_confirmation_bubble_page.gd +++ b/ui/title_confirmation_bubble_page.gd @@ -1,6 +1,8 @@ class_name TitleConfirmationBubblePage extends Control +const QUICK_ENTER_SCALE: float = 0.97 + signal confirmed signal cancelled @@ -17,8 +19,6 @@ signal cancelled Vector2(90.0, 90.0) ) @export var compact_height_threshold: float = 250.0 -@export_range(0.0, 128.0, 1.0) var transition_safe_margin: float = 24.0 - var _cluster: BubbleCluster var _prompt: BubbleButton var _prompt_label: Label @@ -47,6 +47,7 @@ func _ready() -> void: ) _bubbles = [_prompt, _confirm_button, _cancel_button] _cluster.configure(_bubbles) + _cluster.motion_scale = 0.0 _prompt.focus_mode = Control.FOCUS_NONE _prompt.mouse_filter = Control.MOUSE_FILTER_IGNORE _confirm_button.pressed.connect(confirmed.emit) @@ -97,12 +98,13 @@ func set_stage_rect(stage_rect: Rect2) -> void: field_size, false ) + _cluster.pivot_offset = _cluster.size * 0.5 if not _is_transitioning: _cluster.position = _resting_cluster_position func transition_in( - _duration: float, + duration: float, completed: Callable, ) -> void: _transition_generation += 1 @@ -111,21 +113,19 @@ func transition_in( _is_transitioning = true _set_interactive(false) set_process(true) - _cluster.position = Vector2( - _resting_cluster_position.x, - size.y + transition_safe_margin - ) - var duration := UIMotion.bubble_duration( - _cluster.position.y - _resting_cluster_position.y - ) + _cluster.position = _resting_cluster_position + _cluster.pivot_offset = _cluster.size * 0.5 + _cluster.scale = Vector2.ONE * QUICK_ENTER_SCALE + _cluster.modulate.a = 0.0 var generation: int = _transition_generation - _transition = create_tween() + _transition = create_tween().set_parallel(true) _transition.tween_property( _cluster, - "position", - _resting_cluster_position, + "scale", + Vector2.ONE, duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) + _transition.tween_property(_cluster, "modulate:a", 1.0, duration) _transition.finished.connect( _finish_transition_in.bind(generation, completed), CONNECT_ONE_SHOT @@ -133,7 +133,7 @@ func transition_in( func transition_out( - _duration: float, + duration: float, completed: Callable, ) -> void: if not visible or _is_transitioning: @@ -142,18 +142,17 @@ func transition_out( _cancel_transition() _is_transitioning = true _set_interactive(false) - var target_y: float = -_cluster.size.y - transition_safe_margin - var duration := UIMotion.bubble_duration( - target_y - _cluster.position.y - ) + _cluster.position = _resting_cluster_position + _cluster.pivot_offset = _cluster.size * 0.5 var generation: int = _transition_generation - _transition = create_tween() + _transition = create_tween().set_parallel(true) _transition.tween_property( _cluster, - "position:y", - target_y, + "scale", + Vector2.ONE * QUICK_ENTER_SCALE, duration - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + _transition.tween_property(_cluster, "modulate:a", 0.0, duration) _transition.finished.connect( _finish_transition_out.bind(generation, completed), CONNECT_ONE_SHOT @@ -169,6 +168,7 @@ func hide_page() -> void: hide() if _cluster != null: _cluster.position = _resting_cluster_position + _reset_cluster_presentation() func lock_interaction() -> void: @@ -240,6 +240,8 @@ func _finish_transition_in( return _transition = null _is_transitioning = false + _cluster.position = _resting_cluster_position + _reset_cluster_presentation() _set_interactive(true) focus_confirm() completed.call() @@ -255,6 +257,7 @@ func _finish_transition_out( _is_transitioning = false hide() _cluster.position = _resting_cluster_position + _reset_cluster_presentation() set_process(false) completed.call() @@ -263,3 +266,10 @@ func _cancel_transition() -> void: if _transition != null: _transition.kill() _transition = null + + +func _reset_cluster_presentation() -> void: + if _cluster == null: + return + _cluster.scale = Vector2.ONE + _cluster.modulate.a = 1.0 diff --git a/ui/title_screen.gd b/ui/title_screen.gd index bff04d8..242b335 100644 --- a/ui/title_screen.gd +++ b/ui/title_screen.gd @@ -67,13 +67,7 @@ const BUBBLE_WOBBLE_MIN: float = 3.0 const BUBBLE_WOBBLE_MAX: float = 9.0 const BUBBLE_EDGE_MARGIN: float = 16.0 const MAX_DECORATIVE_BUBBLES: int = 6 -const TRANSITION_BUBBLE_COUNT_MIN: int = 4 -const TRANSITION_BUBBLE_COUNT_MAX: int = 5 const MAX_TRANSITION_BUBBLES: int = 30 -const TRANSITION_BUBBLE_X_MIN: float = 0.36 -const TRANSITION_BUBBLE_X_MAX: float = 0.64 -const TRANSITION_BUBBLE_Y_MIN: float = 0.60 -const TRANSITION_BUBBLE_Y_MAX: float = 0.78 const LOGO_ASPECT_RATIO: float = 2560.0 / 760.0 const LOGO_WIDTH_FACTOR: float = 0.4784 const LOGO_MIN_WIDTH: float = 294.0 @@ -89,11 +83,9 @@ const BUBBLE_COMPACT_HEIGHT_THRESHOLD: float = 560.0 const START_PROMPT_MIN_SCALE: float = 0.985 const START_PROMPT_MAX_SCALE: float = 1.015 const START_PROMPT_CYCLE_SECONDS: float = 3.0 -const INTRO_PROMPT_FADE_DURATION: float = 0.55 -const INTRO_BUBBLE_TRAVEL_DURATION: float = 2.40 -const INTRO_BRANDING_TRAVEL_DURATION: float = 2.0 -const INTRO_BRANDING_START_DELAY: float = 0.35 -const HOST_CLUSTER_SAFE_MARGIN: float = 24.0 +const QUICK_MENU_ENTER_DURATION: float = 0.14 +const QUICK_MENU_EXIT_DURATION: float = 0.10 +const QUICK_MENU_ENTER_SCALE: float = 0.97 signal new_game_requested(world_layout: StringName, world_seed: int) signal continue_game_requested @@ -128,13 +120,11 @@ enum ConfirmationAction { @onready var _decorative_bubble_cluster_timer: Timer = %DecorativeBubbleClusterTimer @onready var _title_logo: TextureRect = %TitleLogo @onready var _playtest_label: Label = %PlaytestLabel -@onready var _intro_branding_anchor: Control = %IntroBrandingAnchor @onready var _presentation_center: CenterContainer = %Center @onready var _main_content: VBoxContainer = %MainContent @onready var _branding_slot: Control = %BrandingSlot @onready var _branding_motion_root: Control = %BrandingMotionRoot @onready var _version_row: Control = %VersionRow -@onready var _title_spacer: Control = %Spacer @onready var _button_center: CenterContainer = %ButtonCenter @onready var _bubble_layout_slot: Control = %BubbleLayoutSlot @onready var _bubble_motion_root: Control = %BubbleMotionRoot @@ -176,8 +166,6 @@ var _title_entry_generation: int = 0 var _title_entry_transition_active: bool = false var _intro_geometry_ready: bool = false var _intro_layout_generation: int = 0 -var _branding_intro_position: Vector2 = Vector2.ZERO -var _bubble_intro_position: Vector2 = Vector2.ZERO var _feedback_visible_before_settings: bool = false var _feedback_text_before_settings: String = "" var _continue_stats_hovered: bool = false @@ -227,6 +215,7 @@ func _ready() -> void: ) _credits_page.back_requested.connect(_close_credits) _bubble_field.configure(_get_title_buttons()) + _bubble_field.motion_scale = 0.0 _decorative_fish_timer.timeout.connect(_on_decorative_fish_timer_timeout) _decorative_bubble_event_timer.timeout.connect( _on_decorative_bubble_event_timer_timeout @@ -757,28 +746,30 @@ func _reveal_primary_menu() -> void: set_process(true) _navigation_focus_active = false _release_title_focus() - _title_entry_transition = create_tween() - _title_entry_transition.set_parallel(true) + _bubble_motion_root.position = Vector2.ZERO + _branding_motion_root.position = Vector2.ZERO + _bubble_field.pivot_offset = _bubble_field.size * 0.5 + _bubble_field.scale = Vector2.ONE * QUICK_MENU_ENTER_SCALE + _bubble_field.modulate.a = 0.0 + _title_entry_transition = create_tween().set_parallel(true) _title_entry_transition.tween_property( _start_prompt_center, "modulate:a", 0.0, - INTRO_PROMPT_FADE_DURATION - ).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN_OUT) + QUICK_MENU_EXIT_DURATION + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) _title_entry_transition.tween_property( - _bubble_motion_root, - "position", - Vector2.ZERO, - INTRO_BUBBLE_TRAVEL_DURATION - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + _bubble_field, + "scale", + Vector2.ONE, + QUICK_MENU_ENTER_DURATION + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) _title_entry_transition.tween_property( - _branding_motion_root, - "position", - Vector2.ZERO, - INTRO_BRANDING_TRAVEL_DURATION - ).set_delay(INTRO_BRANDING_START_DELAY).set_trans( - Tween.TRANS_QUAD - ).set_ease(Tween.EASE_IN_OUT) + _bubble_field, + "modulate:a", + 1.0, + QUICK_MENU_ENTER_DURATION + ) _title_entry_transition.finished.connect( _finish_primary_menu_reveal.bind(generation), CONNECT_ONE_SHOT @@ -795,6 +786,8 @@ func _finish_primary_menu_reveal(generation: int) -> void: _title_entry_transition_active = false _start_prompt_center.hide() _start_prompt_center.modulate.a = 1.0 + _bubble_field.scale = Vector2.ONE + _bubble_field.modulate.a = 1.0 _feedback_label.modulate.a = 0.0 _set_title_bubbles_interactive(true) @@ -807,6 +800,10 @@ func _cancel_title_entry_transition() -> void: _title_entry_transition_active = false if is_node_ready(): _start_prompt_center.modulate.a = 1.0 + _bubble_motion_root.position = Vector2.ZERO + _branding_motion_root.position = Vector2.ZERO + _bubble_field.scale = Vector2.ONE + _bubble_field.modulate.a = 1.0 func _schedule_intro_presentation() -> void: @@ -830,59 +827,13 @@ func _prepare_intro_presentation(generation: int) -> void: return _branding_motion_root.size = _branding_slot.size _bubble_motion_root.size = _bubble_layout_slot.size - var intro_tail_height: float = ( - _title_spacer.get_combined_minimum_size().y - + float(_main_content.get_theme_constant("separation")) - ) - var inverse_stage_transform: Transform2D = ( - _title_presentation_scale_root.get_global_transform().affine_inverse() - ) - var branding_anchor_position: Vector2 = ( - inverse_stage_transform - * _intro_branding_anchor.get_global_transform().origin - ) - var branding_slot_position: Vector2 = ( - inverse_stage_transform - * _branding_slot.get_global_transform().origin - ) - var branding_target_position := Vector2( - floorf( - branding_anchor_position.x - - _branding_motion_root.size.x * 0.5 - ), - floorf( - branding_anchor_position.y - - (_branding_motion_root.size.y + intro_tail_height) * 0.5 - ) - ) - _branding_intro_position = ( - branding_target_position - - branding_slot_position - ) - var bubble_bounds: Rect2 = _get_title_bubble_stage_bounds() - bubble_bounds.position -= _bubble_motion_root.position - var presentation_allowance: float = _get_bubble_offscreen_allowance() - _bubble_intro_position = Vector2( - 0.0, - _title_presentation_scale_root.size.y - + presentation_allowance - - bubble_bounds.position.y - ) - _branding_motion_root.position = _branding_intro_position - _bubble_motion_root.position = _bubble_intro_position + _branding_motion_root.position = Vector2.ZERO + _bubble_motion_root.position = Vector2.ZERO _title_content_rest_position = _presentation_center.position _title_bubble_rest_position = _bubble_field.position _intro_geometry_ready = true -func _get_title_bubble_stage_bounds() -> Rect2: - var bubbles: Array[BubbleButton] = _get_title_buttons() - var bounds: Rect2 = _get_title_stage_rect(bubbles[0]) - for index: int in range(1, bubbles.size()): - bounds = bounds.merge(_get_title_stage_rect(bubbles[index])) - return bounds - - func _get_title_stage_rect(control: Control) -> Rect2: var inverse_stage_transform: Transform2D = ( _title_presentation_scale_root.get_global_transform().affine_inverse() @@ -895,30 +846,6 @@ func _get_title_stage_rect(control: Control) -> Rect2: return Rect2(local_position, local_end - local_position) -func _get_bubble_offscreen_allowance() -> float: - var maximum_vertical_amplitude: float = 0.0 - var maximum_deformation_growth: float = 0.0 - for bubble: BubbleButton in _get_title_buttons(): - maximum_vertical_amplitude = maxf( - maximum_vertical_amplitude, - bubble.vertical_amplitude - ) - var maximum_scale: float = ( - 1.0 + bubble.deformation_amplitude - ) * _bubble_field.profile.hover_focus_scale - maximum_deformation_growth = maxf( - maximum_deformation_growth, - bubble.presented_size.y * (maximum_scale - 1.0) * 0.5 - ) - return ( - HOST_CLUSTER_SAFE_MARGIN - + maximum_vertical_amplitude - + maximum_deformation_growth - + _bubble_field.profile.hover_focus_lift - + _bubble_field.profile.maximum_separation - ) - - func _on_continue_stats_hover_changed(hovered: bool) -> void: _continue_stats_hovered = hovered _update_continue_stats_visibility() @@ -975,7 +902,7 @@ func _fade_continue_stats_to(target_opacity: float) -> void: _feedback_label, "modulate:a", target_opacity, - INTRO_PROMPT_FADE_DURATION + QUICK_MENU_ENTER_DURATION ).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN_OUT) @@ -1176,17 +1103,20 @@ func _begin_confirmation_open() -> void: _set_title_bubbles_interactive(false) _release_primary_menu_focus() var generation: int = _confirmation_transition_generation - var content_bounds: Rect2 = _get_title_stage_rect(_main_content) - var exit_distance: float = ( - content_bounds.end.y + HOST_CLUSTER_SAFE_MARGIN - ) - _confirmation_transition = create_tween() + _presentation_center.pivot_offset = _presentation_center.size * 0.5 + _confirmation_transition = create_tween().set_parallel(true) _confirmation_transition.tween_property( _presentation_center, - "position:y", - _confirmation_title_content_rest_position.y - exit_distance, - UIMotion.bubble_duration(exit_distance) - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + "scale", + Vector2.ONE * QUICK_MENU_ENTER_SCALE, + QUICK_MENU_EXIT_DURATION + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + _confirmation_transition.tween_property( + _presentation_center, + "modulate:a", + 0.0, + QUICK_MENU_EXIT_DURATION + ) _confirmation_transition.finished.connect( _finish_confirmation_title_exit.bind(generation), CONNECT_ONE_SHOT @@ -1202,8 +1132,9 @@ func _finish_confirmation_title_exit(generation: int) -> void: _confirmation_transition = null _presentation_center.hide() _presentation_center.position = _confirmation_title_content_rest_position + _reset_quick_menu_presentation(_presentation_center) _confirmation_page.transition_in( - 0.0, + QUICK_MENU_ENTER_DURATION, _finish_confirmation_open.bind(generation) ) @@ -1227,7 +1158,7 @@ func _begin_confirmation_return() -> void: _confirmation_page.lock_interaction() var generation: int = _confirmation_transition_generation _confirmation_page.transition_out( - 0.0, + QUICK_MENU_EXIT_DURATION, _finish_confirmation_page_exit.bind(generation) ) @@ -1239,23 +1170,22 @@ func _finish_confirmation_page_exit(generation: int) -> void: ): return _confirmation_action = ConfirmationAction.NONE - _presentation_center.position = Vector2( - _confirmation_title_content_rest_position.x, - _title_presentation_scale_root.size.y - + HOST_CLUSTER_SAFE_MARGIN - ) + _presentation_center.position = _confirmation_title_content_rest_position _presentation_center.show() - var entry_distance: float = absf( - _presentation_center.position.y - - _confirmation_title_content_rest_position.y - ) - _confirmation_transition = create_tween() + _prepare_quick_menu_enter(_presentation_center) + _confirmation_transition = create_tween().set_parallel(true) _confirmation_transition.tween_property( _presentation_center, - "position", - _confirmation_title_content_rest_position, - UIMotion.bubble_duration(entry_distance) - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + "scale", + Vector2.ONE, + QUICK_MENU_ENTER_DURATION + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) + _confirmation_transition.tween_property( + _presentation_center, + "modulate:a", + 1.0, + QUICK_MENU_ENTER_DURATION + ) _confirmation_transition.finished.connect( _finish_confirmation_return.bind(generation), CONNECT_ONE_SHOT @@ -1271,6 +1201,7 @@ func _finish_confirmation_return(generation: int) -> void: _confirmation_transition = null _confirmation_transition_active = false _presentation_center.position = _confirmation_title_content_rest_position + _reset_quick_menu_presentation(_presentation_center) _set_title_bubbles_interactive(true) var restore_navigation_focus: bool = _modal_restore_navigation_focus _modal_restore_navigation_focus = false @@ -1297,6 +1228,7 @@ func _reset_confirmation() -> void: _confirmation_page.hide_page() if is_node_ready(): _presentation_center.position = _confirmation_title_content_rest_position + _reset_quick_menu_presentation(_presentation_center) _presentation_center.show() @@ -1364,17 +1296,20 @@ func _begin_title_cluster_exit() -> void: _set_title_bubbles_interactive(false) _release_primary_menu_focus() var generation: int = _title_settings_transition_generation - var content_bounds: Rect2 = _get_title_stage_rect(_main_content) - var exit_distance: float = ( - content_bounds.end.y + HOST_CLUSTER_SAFE_MARGIN - ) - _title_settings_transition = create_tween() + _presentation_center.pivot_offset = _presentation_center.size * 0.5 + _title_settings_transition = create_tween().set_parallel(true) _title_settings_transition.tween_property( _presentation_center, - "position:y", - _title_content_rest_position.y - exit_distance, - UIMotion.bubble_duration(exit_distance) - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + "scale", + Vector2.ONE * QUICK_MENU_ENTER_SCALE, + QUICK_MENU_EXIT_DURATION + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + _title_settings_transition.tween_property( + _presentation_center, + "modulate:a", + 0.0, + QUICK_MENU_EXIT_DURATION + ) _title_settings_transition.finished.connect( _finish_title_cluster_exit.bind(generation), CONNECT_ONE_SHOT @@ -1402,6 +1337,7 @@ func _finish_title_cluster_exit(generation: int) -> void: _title_settings_transition_active = false _presentation_center.hide() _presentation_center.position = _title_content_rest_position + _reset_quick_menu_presentation(_presentation_center) func _begin_title_cluster_return(feedback_text: String) -> void: @@ -1415,21 +1351,22 @@ func _begin_title_cluster_return(feedback_text: String) -> void: _hide_continue_stats_context() _set_title_bubbles_interactive(false) var generation: int = _title_settings_transition_generation - _presentation_center.position = Vector2( - _title_content_rest_position.x, - _title_presentation_scale_root.size.y + HOST_CLUSTER_SAFE_MARGIN - ) + _presentation_center.position = _title_content_rest_position _presentation_center.show() - var entry_distance: float = absf( - _presentation_center.position.y - _title_content_rest_position.y - ) - _title_settings_transition = create_tween() + _prepare_quick_menu_enter(_presentation_center) + _title_settings_transition = create_tween().set_parallel(true) _title_settings_transition.tween_property( _presentation_center, - "position", - _title_content_rest_position, - UIMotion.bubble_duration(entry_distance) - ).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + "scale", + Vector2.ONE, + QUICK_MENU_ENTER_DURATION + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) + _title_settings_transition.tween_property( + _presentation_center, + "modulate:a", + 1.0, + QUICK_MENU_ENTER_DURATION + ) _title_settings_transition.finished.connect( _finish_title_cluster_return.bind(generation), CONNECT_ONE_SHOT @@ -1445,6 +1382,7 @@ func _finish_title_cluster_return(generation: int) -> void: _title_settings_transition = null _title_settings_transition_active = false _presentation_center.position = _title_content_rest_position + _reset_quick_menu_presentation(_presentation_center) _bubble_field.position = _title_bubble_rest_position _set_title_bubbles_interactive(true) _restore_settings_focus() @@ -1493,6 +1431,7 @@ func _cancel_title_settings_transition() -> void: _title_settings_transition_active = false if is_node_ready(): _presentation_center.position = _title_content_rest_position + _reset_quick_menu_presentation(_presentation_center) _presentation_center.show() _bubble_field.position = _title_bubble_rest_position _set_title_bubbles_interactive(true) @@ -1504,6 +1443,17 @@ func _cancel_title_settings_tween() -> void: _title_settings_transition = null +func _prepare_quick_menu_enter(control: Control) -> void: + control.pivot_offset = control.size * 0.5 + control.scale = Vector2.ONE * QUICK_MENU_ENTER_SCALE + control.modulate.a = 0.0 + + +func _reset_quick_menu_presentation(control: Control) -> void: + control.scale = Vector2.ONE + control.modulate.a = 1.0 + + func _restore_settings_focus() -> void: var restore_navigation_focus: bool = _modal_restore_navigation_focus _modal_restore_navigation_focus = false @@ -2042,30 +1992,8 @@ func _get_idle_decorative_bubble_count() -> int: func _emit_navigation_bubble_flurry() -> void: - if ( - _minimal_presentation - or not _decorative_presentation_active - or not visible - ): - return - var bubble_count: int = _decorative_rng.randi_range( - TRANSITION_BUBBLE_COUNT_MIN, - TRANSITION_BUBBLE_COUNT_MAX - ) - for _bubble_index: int in bubble_count: - if not _spawn_decorative_bubble( - _decorative_generation, - _decorative_rng.randf_range( - TRANSITION_BUBBLE_Y_MIN, - TRANSITION_BUBBLE_Y_MAX - ), - _decorative_rng.randf_range( - TRANSITION_BUBBLE_X_MIN, - TRANSITION_BUBBLE_X_MAX - ), - true - ): - break + # Page changes now use a quick fade and scale without travel bubbles. + pass func _exit_tree() -> void: