diff --git a/main/main.gd b/main/main.gd index 7894214..21e621f 100644 --- a/main/main.gd +++ b/main/main.gd @@ -24,6 +24,12 @@ const FishingShopType = preload("res://ui/fishing_shop.gd") const FishingShopInteractionType = preload( "res://world/fishing_shop_interaction.gd" ) +const UIPixelationPresenterType = preload( + "res://ui/ui_pixelation_presenter.gd" +) +const PixelationResetOverlayType = preload( + "res://ui/pixelation_reset_overlay.gd" +) const TITLE_MUSIC_SILENCE_DB: float = -80.0 @@ -43,6 +49,10 @@ const TITLE_MUSIC_SILENCE_DB: float = -80.0 @onready var _save_manager: PlayerSaveManagerType = %PlayerSaveManager @onready var _settings_manager: PlayerSettingsManagerType = %PlayerSettingsManager @onready var _title_music: AudioStreamPlayer = %TitleMusic +@onready var _ui_pixelation: UIPixelationPresenterType = %UIPresentation +@onready var _pixelation_reset: PixelationResetOverlayType = ( + %PixelationResetOverlay +) var _gameplay_started: bool = false var _shop_interaction: FishingShopInteractionType @@ -119,6 +129,22 @@ func _ready() -> void: _apply_runtime_settings ): _settings_manager.settings_changed.connect(_apply_runtime_settings) + _ui_pixelation.effective_pixel_size_changed.connect( + _on_effective_ui_pixel_size_changed + ) + _game_ui.pixelation_settings_visibility_changed.connect( + _pixelation_reset.set_settings_open + ) + _game_ui.crisp_reset_focus_requested.connect( + _pixelation_reset.focus_reset_button + ) + _game_ui.interactive_pointer_ui_changed.connect( + _ui_pixelation.set_interactive_ui_open + ) + _pixelation_reset.return_to_settings_requested.connect( + _game_ui.focus_open_settings_back_button + ) + _pixelation_reset.reset_requested.connect(_reset_pixelation) _settings_manager.load_settings() _apply_runtime_settings(_settings_manager.current_settings) _set_gameplay_active(false) @@ -215,6 +241,18 @@ func _process(_delta: float) -> void: func _apply_runtime_settings(settings: PlayerSettingsType) -> void: if settings == null: return + var root_viewport: Viewport = get_viewport() + root_viewport.scaling_3d_mode = Viewport.SCALING_3D_MODE_NEAREST + root_viewport.scaling_3d_scale = PlayerSettingsType.get_world_render_scale( + settings.world_pixel_size + ) + root_viewport.msaa_3d = Viewport.MSAA_DISABLED + root_viewport.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED + root_viewport.use_taa = false + _ui_pixelation.set_pixel_size(settings.ui_pixel_size) + _game_ui.get_title_screen().set_world_pixelation( + settings.world_pixel_size + ) _player.apply_camera_settings( settings.mouse_camera_sensitivity, settings.controller_camera_sensitivity, @@ -226,8 +264,24 @@ func _apply_runtime_settings(settings: PlayerSettingsType) -> void: ) +func _on_effective_ui_pixel_size_changed( + _requested_pixel_size: int, + effective_pixel_size: int, +) -> void: + _game_ui.set_effective_ui_pixel_size(effective_pixel_size) + + +func _reset_pixelation() -> void: + if _settings_manager.apply_pixelation( + PlayerSettingsType.DEFAULT_WORLD_PIXEL_SIZE, + PlayerSettingsType.DEFAULT_UI_PIXEL_SIZE + ): + _game_ui.refresh_open_settings_panel() + + func _set_gameplay_active(active: bool) -> void: _gameplay_started = active + _ui_pixelation.set_gameplay_active(active) if not active: _player.item_effects.reset_all() _player.set_movement_enabled(active) diff --git a/main/main.tscn b/main/main.tscn index d8565ec..9729966 100644 --- a/main/main.tscn +++ b/main/main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=14 format=3] +[gd_scene load_steps=16 format=3] [ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"] [ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"] @@ -13,6 +13,8 @@ [ext_resource type="Resource" path="res://items/catalog/item_catalog.tres" id="11_items"] [ext_resource type="Resource" path="res://economy/buyers/main_fishing_shop.tres" id="12_main_shop"] [ext_resource type="AudioStream" path="res://audio/music/title/as_in_four_wolves.ogg" id="13_title_music"] +[ext_resource type="Script" path="res://ui/ui_pixelation_presenter.gd" id="14_pixelation"] +[ext_resource type="PackedScene" path="res://ui/pixelation_reset_overlay.tscn" id="15_reset"] [node name="Main" type="Node3D"] script = ExtResource("3_main") @@ -51,5 +53,23 @@ stream = ExtResource("13_title_music") volume_db = -80.0 bus = &"Music" -[node name="GameUI" parent="." instance=ExtResource("5_ui")] +[node name="UIPresentation" type="SubViewportContainer" parent="."] +unique_name_in_owner = true +texture_filter = 1 +offset_right = 1152.0 +offset_bottom = 648.0 +mouse_filter = 0 +stretch = true +script = ExtResource("14_pixelation") + +[node name="UIViewport" type="SubViewport" parent="UIPresentation"] +transparent_bg = true +handle_input_locally = false +size = Vector2i(1152, 648) +render_target_update_mode = 4 + +[node name="GameUI" parent="UIPresentation/UIViewport" instance=ExtResource("5_ui")] +unique_name_in_owner = true + +[node name="PixelationResetOverlay" parent="." instance=ExtResource("15_reset")] unique_name_in_owner = true diff --git a/project.godot b/project.godot index 1788218..3b0f605 100644 --- a/project.godot +++ b/project.godot @@ -18,7 +18,7 @@ config/icon="res://icon.svg" [display] -window/stretch/mode="canvas_items" +window/stretch/mode="disabled" window/stretch/aspect="expand" [gui] diff --git a/settings/player_settings.gd b/settings/player_settings.gd index 3c5581a..0d3657e 100644 --- a/settings/player_settings.gd +++ b/settings/player_settings.gd @@ -7,12 +7,22 @@ const MIN_MOUSE_SENSITIVITY: float = 0.001 const MAX_MOUSE_SENSITIVITY: float = 0.012 const MIN_CONTROLLER_SENSITIVITY: float = 0.5 const MAX_CONTROLLER_SENSITIVITY: float = 5.0 +const MIN_WORLD_PIXEL_SIZE: int = 1 +const MAX_WORLD_PIXEL_SIZE: int = 5 +const DEFAULT_WORLD_PIXEL_SIZE: int = 3 +const MIN_UI_PIXEL_SIZE: int = 1 +const MAX_UI_PIXEL_SIZE: int = 5 +const DEFAULT_UI_PIXEL_SIZE: int = 3 +const WORLD_RENDER_SCALES: Array[float] = [1.0, 0.9, 0.8, 0.65, 0.45] +const UI_RENDER_SCALES: Array[float] = [1.0, 0.975, 0.95, 0.925, 0.9] @export var auto_click_enabled: bool = false @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 @export var invert_camera_y: bool = false +@export_range(1, 5, 1) var world_pixel_size: int = DEFAULT_WORLD_PIXEL_SIZE +@export_range(1, 5, 1) var ui_pixel_size: int = DEFAULT_UI_PIXEL_SIZE func is_valid() -> bool: @@ -26,6 +36,10 @@ func is_valid() -> bool: and is_finite(controller_camera_sensitivity) and controller_camera_sensitivity >= MIN_CONTROLLER_SENSITIVITY and controller_camera_sensitivity <= MAX_CONTROLLER_SENSITIVITY + and world_pixel_size >= MIN_WORLD_PIXEL_SIZE + and world_pixel_size <= MAX_WORLD_PIXEL_SIZE + and ui_pixel_size >= MIN_UI_PIXEL_SIZE + and ui_pixel_size <= MAX_UI_PIXEL_SIZE ) @@ -36,4 +50,24 @@ func copy() -> PlayerSettings: result.mouse_camera_sensitivity = mouse_camera_sensitivity result.controller_camera_sensitivity = controller_camera_sensitivity result.invert_camera_y = invert_camera_y + result.world_pixel_size = world_pixel_size + result.ui_pixel_size = ui_pixel_size return result + + +static func get_world_render_scale(pixel_size: int) -> float: + var index: int = clampi( + pixel_size, + MIN_WORLD_PIXEL_SIZE, + MAX_WORLD_PIXEL_SIZE + ) - 1 + return WORLD_RENDER_SCALES[index] + + +static func get_ui_render_scale(pixel_size: int) -> float: + var index: int = clampi( + pixel_size, + MIN_UI_PIXEL_SIZE, + MAX_UI_PIXEL_SIZE + ) - 1 + return UI_RENDER_SCALES[index] diff --git a/settings/player_settings_manager.gd b/settings/player_settings_manager.gd index 47db648..7e989a7 100644 --- a/settings/player_settings_manager.gd +++ b/settings/player_settings_manager.gd @@ -39,6 +39,9 @@ func load_settings() -> bool: return _use_defaults_after_corruption("Player settings structure is invalid.") var accessibility: Dictionary = data["accessibility"] var camera: Dictionary = data["camera"] + var presentation: Dictionary = {} + if typeof(data.get("presentation")) == TYPE_DICTIONARY: + presentation = data["presentation"] if ( typeof(accessibility.get("auto_click_enabled")) != TYPE_BOOL or typeof(camera.get("invert_vertical")) != TYPE_BOOL @@ -59,6 +62,24 @@ func load_settings() -> bool: -1.0 ) loaded.invert_camera_y = camera["invert_vertical"] + loaded.world_pixel_size = _read_clamped_integer( + presentation.get( + "world_pixel_size", + PlayerSettings.DEFAULT_WORLD_PIXEL_SIZE + ), + PlayerSettings.MIN_WORLD_PIXEL_SIZE, + PlayerSettings.MAX_WORLD_PIXEL_SIZE, + PlayerSettings.DEFAULT_WORLD_PIXEL_SIZE + ) + loaded.ui_pixel_size = _read_clamped_integer( + presentation.get( + "ui_pixel_size", + PlayerSettings.DEFAULT_UI_PIXEL_SIZE + ), + PlayerSettings.MIN_UI_PIXEL_SIZE, + PlayerSettings.MAX_UI_PIXEL_SIZE, + PlayerSettings.DEFAULT_UI_PIXEL_SIZE + ) if not loaded.is_valid(): return _use_defaults_after_corruption("Player settings values are invalid.") current_settings = loaded @@ -80,6 +101,21 @@ func apply_settings(settings: PlayerSettings) -> bool: return true +func apply_pixelation(world_pixel_size: int, ui_pixel_size: int) -> bool: + var edited: PlayerSettings = current_settings.copy() + edited.world_pixel_size = clampi( + world_pixel_size, + PlayerSettings.MIN_WORLD_PIXEL_SIZE, + PlayerSettings.MAX_WORLD_PIXEL_SIZE + ) + edited.ui_pixel_size = clampi( + ui_pixel_size, + PlayerSettings.MIN_UI_PIXEL_SIZE, + PlayerSettings.MAX_UI_PIXEL_SIZE + ) + return apply_settings(edited) + + func save_now() -> bool: if current_settings == null or not current_settings.is_valid(): return false @@ -94,6 +130,10 @@ func save_now() -> bool: "controller_sensitivity": current_settings.controller_camera_sensitivity, "invert_vertical": current_settings.invert_camera_y, }, + "presentation": { + "world_pixel_size": current_settings.world_pixel_size, + "ui_pixel_size": current_settings.ui_pixel_size, + }, } var file := FileAccess.open(TEMP_PATH, FileAccess.WRITE) if file == null: @@ -160,6 +200,16 @@ func _read_float(value: Variant, fallback: float) -> float: return result if is_finite(result) else fallback +func _read_clamped_integer( + value: Variant, + minimum: int, + maximum: int, + fallback: int, +) -> int: + var result: int = _read_integer(value, fallback) + return clampi(result, minimum, maximum) + + func _rename_file(from_path: String, to_path: String) -> bool: return DirAccess.rename_absolute( ProjectSettings.globalize_path(from_path), diff --git a/ui/fishing_shop.tscn b/ui/fishing_shop.tscn index 11b062b..793b8e2 100644 --- a/ui/fishing_shop.tscn +++ b/ui/fishing_shop.tscn @@ -38,10 +38,10 @@ anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -offset_left = -480.0 -offset_top = -310.0 -offset_right = 480.0 -offset_bottom = 310.0 +offset_left = -560.0 +offset_top = -320.0 +offset_right = 560.0 +offset_bottom = 320.0 grow_horizontal = 2 grow_vertical = 2 @@ -64,7 +64,7 @@ theme_override_constants/separation = 8 layout_mode = 2 size_flags_horizontal = 3 text = "fishing shop" -theme_override_font_sizes/font_size = 24 +theme_override_font_sizes/font_size = 36 theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1) [node name="CoinIcon" type="TextureRect" parent="ShopPanel/Margin/Layout/Header"] @@ -84,7 +84,7 @@ text = "wallet: $0" unique_name_in_owner = true layout_mode = 2 text = "close" -custom_minimum_size = Vector2(72, 30) +custom_minimum_size = Vector2(110, 58) [node name="Feedback" type="Label" parent="ShopPanel/Margin/Layout"] unique_name_in_owner = true @@ -108,7 +108,7 @@ theme_override_constants/separation = 6 unique_name_in_owner = true layout_mode = 2 text = "sell individual cooler fish • full base value" -theme_override_font_sizes/font_size = 17 +theme_override_font_sizes/font_size = 26 [node name="SalesBody" type="HSplitContainer" parent="ShopPanel/Margin/Layout/Body/FishSales"] layout_mode = 2 @@ -173,7 +173,7 @@ unique_name_in_owner = true layout_mode = 2 text = "select a fish" horizontal_alignment = 1 -theme_override_font_sizes/font_size = 19 +theme_override_font_sizes/font_size = 29 [node name="FishDetails" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"] unique_name_in_owner = true @@ -204,7 +204,7 @@ theme_override_constants/separation = 8 [node name="UpgradeTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades"] layout_mode = 2 text = "fishing upgrades" -theme_override_font_sizes/font_size = 17 +theme_override_font_sizes/font_size = 26 [node name="Supplies" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"] custom_minimum_size = Vector2(210, 0) @@ -214,7 +214,7 @@ theme_override_constants/separation = 6 [node name="Title" type="Label" parent="ShopPanel/Margin/Layout/Body/Supplies"] layout_mode = 2 text = "supplies" -theme_override_font_sizes/font_size = 17 +theme_override_font_sizes/font_size = 26 [node name="Scroll" type="ScrollContainer" parent="ShopPanel/Margin/Layout/Body/Supplies"] layout_mode = 2 @@ -256,7 +256,7 @@ size_flags_horizontal = 3 [node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"] layout_mode = 2 text = "reel speed" -theme_override_font_sizes/font_size = 17 +theme_override_font_sizes/font_size = 26 [node name="ReelLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"] unique_name_in_owner = true @@ -307,7 +307,7 @@ size_flags_horizontal = 3 [node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"] layout_mode = 2 text = "barrier power" -theme_override_font_sizes/font_size = 17 +theme_override_font_sizes/font_size = 26 [node name="BarrierLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"] unique_name_in_owner = true @@ -358,7 +358,7 @@ size_flags_horizontal = 3 [node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"] layout_mode = 2 text = "cooler capacity" -theme_override_font_sizes/font_size = 17 +theme_override_font_sizes/font_size = 26 [node name="CoolerLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"] unique_name_in_owner = true @@ -390,10 +390,10 @@ anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -offset_left = -220.0 -offset_top = -90.0 -offset_right = 220.0 -offset_bottom = 90.0 +offset_left = -320.0 +offset_top = -150.0 +offset_right = 320.0 +offset_bottom = 150.0 grow_horizontal = 2 grow_vertical = 2 diff --git a/ui/game_theme.tres b/ui/game_theme.tres index 1021bc8..a7d4282 100644 --- a/ui/game_theme.tres +++ b/ui/game_theme.tres @@ -146,7 +146,7 @@ corner_radius_bottom_left = 6 [resource] default_font = ExtResource("1_font") -default_font_size = 15 +default_font_size = 23 Label/colors/font_color = Color(0.956, 0.972, 0.976, 1) Label/colors/font_shadow_color = Color(0, 0, 0, 0) PanelContainer/styles/panel = SubResource("Panel") diff --git a/ui/game_ui.gd b/ui/game_ui.gd index 0301989..d32184a 100644 --- a/ui/game_ui.gd +++ b/ui/game_ui.gd @@ -17,6 +17,7 @@ const HotbarUIType = preload("res://ui/hotbar.gd") const TitleScreenType = preload("res://ui/title_screen.gd") const PauseMenuType = preload("res://ui/pause_menu.gd") const FishingShopType = preload("res://ui/fishing_shop.gd") +const SettingsPanelType = preload("res://ui/settings_panel.gd") const PlayerFishingUpgradesType = preload( "res://progression/player_fishing_upgrades.gd" ) @@ -30,6 +31,10 @@ const PlayerCoolerCapacityType = preload( "res://progression/player_cooler_capacity.gd" ) +signal pixelation_settings_visibility_changed(is_visible: bool) +signal crisp_reset_focus_requested +signal interactive_pointer_ui_changed(is_open: bool) + @onready var _status_label: Label = %StatusLabel @onready var _catch_track: Control = %CatchTrack @onready var _green_catch_progress: ProgressBar = %GreenCatchProgress @@ -47,6 +52,12 @@ const PlayerCoolerCapacityType = preload( @onready var _fishing_shop: FishingShopType = %FishingShop @onready var _shop_prompt: PanelContainer = %ShopPrompt @onready var _effect_status: Label = %EffectStatus +@onready var _title_settings_panel: SettingsPanelType = ( + $UIRoot/TitleScreen/SettingsPanel +) +@onready var _pause_settings_panel: SettingsPanelType = ( + $UIRoot/PauseMenu/SettingsCenter/SettingsPanel +) var _showcase_active: bool = false var _player_menu_open: bool = false @@ -57,6 +68,21 @@ var _shop_open: bool = false var _item_effects: PlayerItemEffectsType +func _ready() -> void: + _title_settings_panel.panel_visibility_changed.connect( + _on_settings_visibility_changed + ) + _pause_settings_panel.panel_visibility_changed.connect( + _on_settings_visibility_changed + ) + _title_settings_panel.crisp_reset_focus_requested.connect( + crisp_reset_focus_requested.emit + ) + _pause_settings_panel.crisp_reset_focus_requested.connect( + crisp_reset_focus_requested.emit + ) + + func setup( player: PlayerType, inventory: FishInventoryType, @@ -195,6 +221,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: _shop_prompt.hide() + _emit_interactive_pointer_ui_changed() func get_fishing_shop() -> FishingShopType: @@ -219,6 +246,29 @@ func get_title_screen() -> TitleScreenType: return _title_screen +func set_effective_ui_pixel_size(pixel_size: int) -> void: + _title_settings_panel.set_effective_ui_pixel_size(pixel_size) + _pause_settings_panel.set_effective_ui_pixel_size(pixel_size) + + +func focus_open_settings_back_button() -> void: + if _title_settings_panel.visible: + _title_settings_panel.focus_back_button() + elif _pause_settings_panel.visible: + _pause_settings_panel.focus_back_button() + + +func refresh_open_settings_panel() -> void: + _title_settings_panel.refresh_open_panel() + _pause_settings_panel.refresh_open_panel() + + +func _on_settings_visibility_changed(_is_visible: bool) -> void: + pixelation_settings_visibility_changed.emit( + _title_settings_panel.visible or _pause_settings_panel.visible + ) + + func _on_fishing_status_changed(status: String) -> void: if _showcase_active: return @@ -391,6 +441,7 @@ func _on_player_menu_visibility_changed(is_open: bool) -> void: ) _hotbar_ui.set_drag_enabled(is_open) _refresh_fishing_panel_visibility() + _emit_interactive_pointer_ui_changed() if is_open: _shop_prompt.hide() @@ -408,3 +459,10 @@ func _on_shop_visibility_changed(is_open: bool) -> void: ) if is_open: _shop_prompt.hide() + _emit_interactive_pointer_ui_changed() + + +func _emit_interactive_pointer_ui_changed() -> void: + interactive_pointer_ui_changed.emit( + _system_menu_open or _player_menu_open or _shop_open + ) diff --git a/ui/game_ui.tscn b/ui/game_ui.tscn index 18b7864..6c0740b 100644 --- a/ui/game_ui.tscn +++ b/ui/game_ui.tscn @@ -36,7 +36,11 @@ bg_color = Color(0, 0, 0, 0) [node name="GameUI" type="CanvasLayer"] script = ExtResource("1_ui") -[node name="FishingPanel" type="PanelContainer" parent="."] +[node name="UIRoot" type="Control" parent="."] +unique_name_in_owner = true +mouse_filter = 2 + +[node name="FishingPanel" type="PanelContainer" parent="UIRoot"] unique_name_in_owner = true visible = false z_index = 60 @@ -54,33 +58,33 @@ grow_vertical = 0 mouse_filter = 2 theme = ExtResource("3_theme") -[node name="MarginContainer" type="MarginContainer" parent="FishingPanel"] +[node name="MarginContainer" type="MarginContainer" parent="UIRoot/FishingPanel"] theme_override_constants/margin_left = 14 theme_override_constants/margin_top = 8 theme_override_constants/margin_right = 14 theme_override_constants/margin_bottom = 8 -[node name="VBoxContainer" type="VBoxContainer" parent="FishingPanel/MarginContainer"] +[node name="VBoxContainer" type="VBoxContainer" parent="UIRoot/FishingPanel/MarginContainer"] theme_override_constants/separation = 4 -[node name="StatusLabel" type="Label" parent="FishingPanel/MarginContainer/VBoxContainer"] +[node name="StatusLabel" type="Label" parent="UIRoot/FishingPanel/MarginContainer/VBoxContainer"] unique_name_in_owner = true visible = false text = "" horizontal_alignment = 1 theme_override_font_sizes/font_size = 16 -[node name="ShowcaseDetails" type="Label" parent="FishingPanel/MarginContainer/VBoxContainer"] +[node name="ShowcaseDetails" type="Label" parent="UIRoot/FishingPanel/MarginContainer/VBoxContainer"] unique_name_in_owner = true visible = false horizontal_alignment = 1 -[node name="CatchTrack" type="Control" parent="FishingPanel/MarginContainer/VBoxContainer"] +[node name="CatchTrack" type="Control" parent="UIRoot/FishingPanel/MarginContainer/VBoxContainer"] unique_name_in_owner = true visible = false custom_minimum_size = Vector2(0, 20) -[node name="GreenCatchProgress" type="ProgressBar" parent="FishingPanel/MarginContainer/VBoxContainer/CatchTrack"] +[node name="GreenCatchProgress" type="ProgressBar" parent="UIRoot/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"] unique_name_in_owner = true layout_mode = 1 anchors_preset = 15 @@ -94,7 +98,7 @@ theme_override_styles/fill = SubResource("StyleBox_catch_fill") value = 0.0 show_percentage = false -[node name="RedChaseProgress" type="ProgressBar" parent="FishingPanel/MarginContainer/VBoxContainer/CatchTrack"] +[node name="RedChaseProgress" type="ProgressBar" parent="UIRoot/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"] unique_name_in_owner = true layout_mode = 1 anchors_preset = 15 @@ -108,7 +112,7 @@ theme_override_styles/fill = SubResource("StyleBox_chase_fill") value = 0.0 show_percentage = false -[node name="BarrierMarkers" type="Control" parent="FishingPanel/MarginContainer/VBoxContainer/CatchTrack"] +[node name="BarrierMarkers" type="Control" parent="UIRoot/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"] unique_name_in_owner = true layout_mode = 1 anchors_preset = 15 @@ -118,23 +122,23 @@ grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 -[node name="BarrierSummary" type="Label" parent="FishingPanel/MarginContainer/VBoxContainer"] +[node name="BarrierSummary" type="Label" parent="UIRoot/FishingPanel/MarginContainer/VBoxContainer"] unique_name_in_owner = true visible = false horizontal_alignment = 1 -[node name="BarrierHealth" type="Label" parent="FishingPanel/MarginContainer/VBoxContainer"] +[node name="BarrierHealth" type="Label" parent="UIRoot/FishingPanel/MarginContainer/VBoxContainer"] unique_name_in_owner = true visible = false horizontal_alignment = 1 -[node name="PlayerMenu" parent="." instance=ExtResource("2_menu")] +[node name="PlayerMenu" parent="UIRoot" instance=ExtResource("2_menu")] unique_name_in_owner = true -[node name="Hotbar" parent="." instance=ExtResource("7_hotbar")] +[node name="Hotbar" parent="UIRoot" instance=ExtResource("7_hotbar")] unique_name_in_owner = true -[node name="ShopPrompt" type="PanelContainer" parent="."] +[node name="ShopPrompt" type="PanelContainer" parent="UIRoot"] unique_name_in_owner = true visible = false z_index = 55 @@ -152,22 +156,22 @@ grow_vertical = 0 mouse_filter = 2 theme = ExtResource("3_theme") -[node name="Margin" type="MarginContainer" parent="ShopPrompt"] +[node name="Margin" type="MarginContainer" parent="UIRoot/ShopPrompt"] layout_mode = 2 theme_override_constants/margin_left = 12 theme_override_constants/margin_top = 7 theme_override_constants/margin_right = 12 theme_override_constants/margin_bottom = 7 -[node name="Label" type="Label" parent="ShopPrompt/Margin"] +[node name="Label" type="Label" parent="UIRoot/ShopPrompt/Margin"] layout_mode = 2 text = "press e to open fishing shop" horizontal_alignment = 1 -[node name="FishingShop" parent="." instance=ExtResource("8_shop")] +[node name="FishingShop" parent="UIRoot" instance=ExtResource("8_shop")] unique_name_in_owner = true -[node name="EffectStatus" type="Label" parent="."] +[node name="EffectStatus" type="Label" parent="UIRoot"] unique_name_in_owner = true visible = false z_index = 54 @@ -181,7 +185,7 @@ theme_override_colors/font_shadow_color = Color(0, 0, 0, 0.9) theme_override_constants/shadow_offset_x = 2 theme_override_constants/shadow_offset_y = 2 -[node name="ScreenFade" type="ColorRect" parent="."] +[node name="ScreenFade" type="ColorRect" parent="UIRoot"] unique_name_in_owner = true visible = false z_index = 100 @@ -194,8 +198,8 @@ mouse_filter = 0 color = Color(0, 0, 0, 1) script = ExtResource("4_fade") -[node name="TitleScreen" parent="." instance=ExtResource("5_title")] +[node name="TitleScreen" parent="UIRoot" instance=ExtResource("5_title")] unique_name_in_owner = true -[node name="PauseMenu" parent="." instance=ExtResource("6_pause")] +[node name="PauseMenu" parent="UIRoot" instance=ExtResource("6_pause")] unique_name_in_owner = true diff --git a/ui/pause_menu.tscn b/ui/pause_menu.tscn index e94252a..ec504ac 100644 --- a/ui/pause_menu.tscn +++ b/ui/pause_menu.tscn @@ -38,7 +38,7 @@ grow_vertical = 2 [node name="RootPanel" type="PanelContainer" parent="RootCenter"] unique_name_in_owner = true -custom_minimum_size = Vector2(420, 0) +custom_minimum_size = Vector2(560, 0) layout_mode = 2 [node name="Margin" type="MarginContainer" parent="RootCenter/RootPanel"] @@ -54,50 +54,50 @@ theme_override_constants/separation = 11 [node name="Title" type="Label" parent="RootCenter/RootPanel/Margin/Content"] layout_mode = 2 -theme_override_font_sizes/font_size = 30 +theme_override_font_sizes/font_size = 45 text = "game menu" horizontal_alignment = 1 [node name="ResumeButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 43) +custom_minimum_size = Vector2(0, 64) layout_mode = 2 text = "return to game" [node name="SaveButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 43) +custom_minimum_size = Vector2(0, 64) layout_mode = 2 text = "save now" [node name="SettingsButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 43) +custom_minimum_size = Vector2(0, 64) layout_mode = 2 text = "settings" [node name="ReturnToTitleButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 43) +custom_minimum_size = Vector2(0, 64) layout_mode = 2 text = "return to title" [node name="ResetProgressButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 43) +custom_minimum_size = Vector2(0, 64) layout_mode = 2 theme_type_variation = &"DangerButton" text = "reset progress" [node name="QuitButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 43) +custom_minimum_size = Vector2(0, 64) layout_mode = 2 text = "quit" [node name="FeedbackLabel" type="Label" parent="RootCenter/RootPanel/Margin/Content"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 34) +custom_minimum_size = Vector2(0, 54) layout_mode = 2 horizontal_alignment = 1 vertical_alignment = 1 @@ -128,7 +128,7 @@ mouse_filter = 2 [node name="ConfirmationPanel" type="PanelContainer" parent="ConfirmationCenter"] unique_name_in_owner = true visible = false -custom_minimum_size = Vector2(520, 0) +custom_minimum_size = Vector2(700, 0) layout_mode = 2 [node name="Margin" type="MarginContainer" parent="ConfirmationCenter/ConfirmationPanel"] @@ -146,7 +146,7 @@ theme_override_constants/separation = 15 unique_name_in_owner = true layout_mode = 2 theme_override_colors/font_color = Color(0.937, 0.357, 0.384, 1) -theme_override_font_sizes/font_size = 24 +theme_override_font_sizes/font_size = 36 text = "confirm" horizontal_alignment = 1 @@ -164,12 +164,12 @@ alignment = 1 [node name="ConfirmButton" type="Button" parent="ConfirmationCenter/ConfirmationPanel/Margin/Content/Buttons"] unique_name_in_owner = true -custom_minimum_size = Vector2(190, 43) +custom_minimum_size = Vector2(240, 64) layout_mode = 2 text = "confirm" [node name="CancelConfirmButton" type="Button" parent="ConfirmationCenter/ConfirmationPanel/Margin/Content/Buttons"] unique_name_in_owner = true -custom_minimum_size = Vector2(130, 43) +custom_minimum_size = Vector2(180, 64) layout_mode = 2 text = "cancel" diff --git a/ui/pixelation_reset_overlay.gd b/ui/pixelation_reset_overlay.gd new file mode 100644 index 0000000..8aea79a --- /dev/null +++ b/ui/pixelation_reset_overlay.gd @@ -0,0 +1,32 @@ +class_name PixelationResetOverlay +extends CanvasLayer + +signal reset_requested +signal return_to_settings_requested + +@onready var _reset_button: Button = %ResetPixelationButton + + +func _ready() -> void: + _reset_button.pressed.connect(reset_requested.emit) + _reset_button.gui_input.connect(_on_reset_button_gui_input) + + +func set_settings_open(is_open: bool) -> void: + visible = is_open + if not is_open and _reset_button.has_focus(): + _reset_button.release_focus() + + +func focus_reset_button() -> void: + if visible: + _reset_button.grab_focus() + + +func _on_reset_button_gui_input(event: InputEvent) -> void: + if ( + event.is_action_pressed("ui_up") + or event.is_action_pressed("ui_focus_prev") + ): + return_to_settings_requested.emit() + get_viewport().set_input_as_handled() diff --git a/ui/pixelation_reset_overlay.gd.uid b/ui/pixelation_reset_overlay.gd.uid new file mode 100644 index 0000000..c17c7e7 --- /dev/null +++ b/ui/pixelation_reset_overlay.gd.uid @@ -0,0 +1 @@ +uid://bg7x4pmgy6mhn diff --git a/ui/pixelation_reset_overlay.tscn b/ui/pixelation_reset_overlay.tscn new file mode 100644 index 0000000..cfb1b99 --- /dev/null +++ b/ui/pixelation_reset_overlay.tscn @@ -0,0 +1,30 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Script" path="res://ui/pixelation_reset_overlay.gd" id="1_script"] +[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] + +[node name="PixelationResetOverlay" type="CanvasLayer"] +layer = 300 +visible = false +script = ExtResource("1_script") + +[node name="ResetAnchor" type="MarginContainer" parent="."] +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -324.0 +offset_top = -86.0 +offset_right = -16.0 +offset_bottom = -16.0 +grow_horizontal = 0 +grow_vertical = 0 +mouse_filter = 2 +theme = ExtResource("2_theme") + +[node name="ResetPixelationButton" type="Button" parent="ResetAnchor"] +unique_name_in_owner = true +custom_minimum_size = Vector2(308, 70) +layout_mode = 2 +text = "reset pixelation" diff --git a/ui/player_menu.gd b/ui/player_menu.gd index 79b1fd0..bc7d207 100644 --- a/ui/player_menu.gd +++ b/ui/player_menu.gd @@ -587,7 +587,7 @@ func _create_inventory_card(fish_catch: FishCatchType) -> Button: "font_color", UIPalette.SECONDARY ) - favorite_marker.add_theme_font_size_override("font_size", 18) + 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 @@ -603,7 +603,7 @@ func _create_inventory_card(fish_catch: FishCatchType) -> Button: "font_color", UIPalette.SUCCESS ) - selected_marker.add_theme_font_size_override("font_size", 18) + 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 diff --git a/ui/player_menu.tscn b/ui/player_menu.tscn index 3dccbbe..824eda6 100644 --- a/ui/player_menu.tscn +++ b/ui/player_menu.tscn @@ -29,10 +29,10 @@ color = Color(0.015, 0.02, 0.03, 0.72) [node name="MenuPanel" type="PanelContainer" parent="."] layout_mode = 1 -anchor_left = 0.07 -anchor_top = 0.055 -anchor_right = 0.93 -anchor_bottom = 0.72 +anchor_left = 0.02 +anchor_top = 0.025 +anchor_right = 0.98 +anchor_bottom = 0.96 grow_horizontal = 2 grow_vertical = 2 @@ -55,7 +55,7 @@ theme_override_constants/separation = 8 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 = 22 +theme_override_font_sizes/font_size = 33 text = "player menu" [node name="WalletBalance" type="Label" parent="MenuPanel/Margin/Layout/Header"] @@ -69,27 +69,27 @@ unique_name_in_owner = true layout_mode = 2 toggle_mode = true text = "cooler" -custom_minimum_size = Vector2(88, 30) +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(76, 30) +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(88, 30) +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(68, 30) +custom_minimum_size = Vector2(110, 58) [node name="Separator" type="HSeparator" parent="MenuPanel/Margin/Layout"] layout_mode = 2 @@ -100,7 +100,7 @@ layout_mode = 2 text = "" horizontal_alignment = 1 theme_override_colors/font_color = Color(1, 0.82, 0.4, 1) -theme_override_font_sizes/font_size = 14 +theme_override_font_sizes/font_size = 21 [node name="Content" type="Control" parent="MenuPanel/Margin/Layout"] custom_minimum_size = Vector2(0, 260) @@ -128,12 +128,12 @@ 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(130, 30) +custom_minimum_size = Vector2(190, 58) [node name="SortDirection" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"] unique_name_in_owner = true layout_mode = 2 -custom_minimum_size = Vector2(112, 30) +custom_minimum_size = Vector2(190, 58) text = "newest first" [node name="SortSpacer" type="Control" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"] @@ -219,7 +219,7 @@ texture_filter = 1 unique_name_in_owner = true layout_mode = 2 theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1) -theme_override_font_sizes/font_size = 19 +theme_override_font_sizes/font_size = 29 horizontal_alignment = 1 [node name="DetailData" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"] @@ -339,7 +339,7 @@ texture_filter = 1 [node name="BagDetailName" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagDetail/Margin/Stack"] unique_name_in_owner = true layout_mode = 2 -theme_override_font_sizes/font_size = 19 +theme_override_font_sizes/font_size = 29 horizontal_alignment = 1 [node name="BagDetailData" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagDetail/Margin/Stack"] @@ -389,10 +389,10 @@ anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -offset_left = -230.0 -offset_top = -105.0 -offset_right = 230.0 -offset_bottom = 105.0 +offset_left = -320.0 +offset_top = -160.0 +offset_right = 320.0 +offset_bottom = 160.0 grow_horizontal = 2 grow_vertical = 2 @@ -422,10 +422,10 @@ unique_name_in_owner = true layout_mode = 2 text = "confirm" theme_type_variation = &"DangerButton" -custom_minimum_size = Vector2(120, 38) +custom_minimum_size = Vector2(170, 64) [node name="CancelSaleButton" type="Button" parent="MenuPanel/Margin/Layout/Content/SaleConfirmation/ConfirmationMargin/ConfirmationLayout/ConfirmationButtons"] unique_name_in_owner = true layout_mode = 2 text = "cancel" -custom_minimum_size = Vector2(120, 38) +custom_minimum_size = Vector2(170, 64) diff --git a/ui/settings_panel.gd b/ui/settings_panel.gd index d31cb19..1ec189d 100644 --- a/ui/settings_panel.gd +++ b/ui/settings_panel.gd @@ -1,12 +1,16 @@ class_name SettingsPanel extends PanelContainer +const MAX_PANEL_SIZE: Vector2 = Vector2(720.0, 600.0) +const PANEL_EDGE_MARGIN: float = 16.0 const SettingsManagerType = preload( "res://settings/player_settings_manager.gd" ) signal applied signal closed +signal crisp_reset_focus_requested +signal panel_visibility_changed(is_visible: bool) @onready var _auto_click_toggle: CheckButton = %AutoClickToggle @onready var _auto_click_interval: HSlider = %AutoClickInterval @@ -16,9 +20,15 @@ signal closed @onready var _controller_sensitivity: HSlider = %ControllerSensitivity @onready var _controller_sensitivity_value: Label = %ControllerSensitivityValue @onready var _invert_y_toggle: CheckButton = %InvertYToggle +@onready var _world_pixel_size: HSlider = %WorldPixelSize +@onready var _world_pixel_size_value: Label = %WorldPixelSizeValue +@onready var _ui_pixel_size: HSlider = %UIPixelSize +@onready var _ui_pixel_size_value: Label = %UIPixelSizeValue @onready var _feedback: Label = %SettingsFeedback var _settings_manager: SettingsManagerType +var _loading_controls: bool = false +var _effective_ui_pixel_size: int = PlayerSettings.DEFAULT_UI_PIXEL_SIZE func _ready() -> void: @@ -27,6 +37,14 @@ func _ready() -> void: _auto_click_interval.value_changed.connect(_refresh_value_labels) _mouse_sensitivity.value_changed.connect(_refresh_value_labels) _controller_sensitivity.value_changed.connect(_refresh_value_labels) + _world_pixel_size.value_changed.connect(_on_pixel_size_changed) + _ui_pixel_size.value_changed.connect(_on_pixel_size_changed) + %CancelSettingsButton.gui_input.connect(_on_back_button_gui_input) + get_viewport().size_changed.connect(_refresh_panel_size) + var parent_control := get_parent() as Control + if parent_control != null: + parent_control.resized.connect(_refresh_panel_size) + call_deferred("_refresh_panel_size") func open_panel(settings_manager: SettingsManagerType) -> void: @@ -34,6 +52,7 @@ func open_panel(settings_manager: SettingsManagerType) -> void: _load_controls() _feedback.text = "" show() + panel_visibility_changed.emit(true) _auto_click_toggle.grab_focus() @@ -41,6 +60,7 @@ func close_panel() -> void: if not visible: return hide() + panel_visibility_changed.emit(false) _load_controls() closed.emit() @@ -55,8 +75,11 @@ func _apply_settings() -> void: edited.mouse_camera_sensitivity = float(_mouse_sensitivity.value) edited.controller_camera_sensitivity = float(_controller_sensitivity.value) edited.invert_camera_y = _invert_y_toggle.button_pressed + edited.world_pixel_size = roundi(_world_pixel_size.value) + edited.ui_pixel_size = roundi(_ui_pixel_size.value) if _settings_manager.apply_settings(edited): hide() + panel_visibility_changed.emit(false) applied.emit() else: _feedback.text = "failed to save settings." @@ -65,12 +88,16 @@ func _apply_settings() -> void: func _load_controls() -> void: if _settings_manager == null or not is_node_ready(): return + _loading_controls = true var settings: PlayerSettings = _settings_manager.current_settings _auto_click_toggle.button_pressed = settings.auto_click_enabled _auto_click_interval.value = settings.auto_click_interval _mouse_sensitivity.value = settings.mouse_camera_sensitivity _controller_sensitivity.value = settings.controller_camera_sensitivity _invert_y_toggle.button_pressed = settings.invert_camera_y + _world_pixel_size.value = settings.world_pixel_size + _ui_pixel_size.value = settings.ui_pixel_size + _loading_controls = false _refresh_value_labels(0.0) @@ -78,3 +105,83 @@ func _refresh_value_labels(_unused: float) -> void: _auto_click_interval_value.text = "%.2f s" % _auto_click_interval.value _mouse_sensitivity_value.text = "%.4f" % _mouse_sensitivity.value _controller_sensitivity_value.text = "%.1f" % _controller_sensitivity.value + _world_pixel_size_value.text = _pixel_size_label( + roundi(_world_pixel_size.value), + true + ) + _ui_pixel_size_value.text = _pixel_size_label( + roundi(_ui_pixel_size.value), + false + ) + + +func _on_pixel_size_changed(_unused: float) -> void: + _refresh_value_labels(0.0) + if _loading_controls or _settings_manager == null: + return + if not _settings_manager.apply_pixelation( + roundi(_world_pixel_size.value), + roundi(_ui_pixel_size.value) + ): + _feedback.text = "failed to save pixelation settings." + + +func _pixel_size_label(pixel_size: int, _is_world: bool) -> String: + var names: PackedStringArray = [ + "legible", + "cute", + "retro", + "hardcore", + "wtf", + ] + var index: int = clampi(pixel_size - 1, 0, names.size() - 1) + return names[index] + + +func set_effective_ui_pixel_size(pixel_size: int) -> void: + _effective_ui_pixel_size = clampi( + pixel_size, + PlayerSettings.MIN_UI_PIXEL_SIZE, + PlayerSettings.MAX_UI_PIXEL_SIZE + ) + if is_node_ready(): + _refresh_value_labels(0.0) + + +func focus_back_button() -> void: + %CancelSettingsButton.grab_focus() + + +func refresh_open_panel() -> void: + if visible: + _load_controls() + + +func _on_back_button_gui_input(event: InputEvent) -> void: + if ( + event.is_action_pressed("ui_down") + or event.is_action_pressed("ui_focus_next") + ): + crisp_reset_focus_requested.emit() + accept_event() + + +func _refresh_panel_size() -> void: + if not is_node_ready(): + return + var parent_control := get_parent() as Control + if parent_control == null: + return + var available_size: Vector2 = parent_control.size - Vector2.ONE * ( + PANEL_EDGE_MARGIN * 2.0 + ) + var target_size := Vector2( + minf(MAX_PANEL_SIZE.x, maxf(320.0, available_size.x)), + minf(MAX_PANEL_SIZE.y, maxf(240.0, available_size.y)) + ) + custom_minimum_size = target_size + if parent_control is CenterContainer: + return + set_anchors_preset(Control.PRESET_CENTER) + position = (parent_control.size - target_size) * 0.5 + size = target_size diff --git a/ui/settings_panel.tscn b/ui/settings_panel.tscn index cfdef8e..0d708cb 100644 --- a/ui/settings_panel.tscn +++ b/ui/settings_panel.tscn @@ -5,7 +5,7 @@ [node name="SettingsPanel" type="PanelContainer"] visible = false -custom_minimum_size = Vector2(600, 490) +custom_minimum_size = Vector2(720, 600) theme = ExtResource("2_theme") script = ExtResource("1_script") @@ -16,35 +16,42 @@ theme_override_constants/margin_top = 24 theme_override_constants/margin_right = 28 theme_override_constants/margin_bottom = 24 -[node name="Content" type="VBoxContainer" parent="Margin"] +[node name="Scroll" type="ScrollContainer" parent="Margin"] layout_mode = 2 +horizontal_scroll_mode = 0 + +[node name="Content" type="VBoxContainer" parent="Margin/Scroll"] +layout_mode = 2 +size_flags_horizontal = 3 theme_override_constants/separation = 12 -[node name="Heading" type="Label" parent="Margin/Content"] +[node name="Heading" type="Label" parent="Margin/Scroll/Content"] layout_mode = 2 -theme_override_font_sizes/font_size = 26 +theme_override_font_sizes/font_size = 39 text = "settings" horizontal_alignment = 1 -[node name="AutoClickToggle" type="CheckButton" parent="Margin/Content"] +[node name="AutoClickToggle" type="CheckButton" parent="Margin/Scroll/Content"] unique_name_in_owner = true layout_mode = 2 text = "accessibility auto-click" -[node name="AutoClickHelp" type="Label" parent="Margin/Content"] +[node name="AutoClickHelp" type="Label" parent="Margin/Scroll/Content"] layout_mode = 2 text = "lower intervals click barriers faster while held." +autowrap_mode = 2 -[node name="AutoClickRow" type="HBoxContainer" parent="Margin/Content"] +[node name="AutoClickRow" type="HBoxContainer" parent="Margin/Scroll/Content"] layout_mode = 2 theme_override_constants/separation = 12 -[node name="Label" type="Label" parent="Margin/Content/AutoClickRow"] -custom_minimum_size = Vector2(190, 0) +[node name="Label" type="Label" parent="Margin/Scroll/Content/AutoClickRow"] +custom_minimum_size = Vector2(260, 0) layout_mode = 2 text = "auto-click interval" +autowrap_mode = 2 -[node name="AutoClickInterval" type="HSlider" parent="Margin/Content/AutoClickRow"] +[node name="AutoClickInterval" type="HSlider" parent="Margin/Scroll/Content/AutoClickRow"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -53,22 +60,23 @@ max_value = 0.5 step = 0.01 value = 0.2 -[node name="AutoClickIntervalValue" type="Label" parent="Margin/Content/AutoClickRow"] +[node name="AutoClickIntervalValue" type="Label" parent="Margin/Scroll/Content/AutoClickRow"] unique_name_in_owner = true -custom_minimum_size = Vector2(62, 0) +custom_minimum_size = Vector2(100, 0) layout_mode = 2 text = "0.20 s" -[node name="MouseRow" type="HBoxContainer" parent="Margin/Content"] +[node name="MouseRow" type="HBoxContainer" parent="Margin/Scroll/Content"] layout_mode = 2 theme_override_constants/separation = 12 -[node name="Label" type="Label" parent="Margin/Content/MouseRow"] -custom_minimum_size = Vector2(190, 0) +[node name="Label" type="Label" parent="Margin/Scroll/Content/MouseRow"] +custom_minimum_size = Vector2(260, 0) layout_mode = 2 text = "mouse camera sensitivity" +autowrap_mode = 2 -[node name="MouseSensitivity" type="HSlider" parent="Margin/Content/MouseRow"] +[node name="MouseSensitivity" type="HSlider" parent="Margin/Scroll/Content/MouseRow"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -77,22 +85,23 @@ max_value = 0.012 step = 0.0005 value = 0.005 -[node name="MouseSensitivityValue" type="Label" parent="Margin/Content/MouseRow"] +[node name="MouseSensitivityValue" type="Label" parent="Margin/Scroll/Content/MouseRow"] unique_name_in_owner = true -custom_minimum_size = Vector2(62, 0) +custom_minimum_size = Vector2(100, 0) layout_mode = 2 text = "0.0050" -[node name="ControllerRow" type="HBoxContainer" parent="Margin/Content"] +[node name="ControllerRow" type="HBoxContainer" parent="Margin/Scroll/Content"] layout_mode = 2 theme_override_constants/separation = 12 -[node name="Label" type="Label" parent="Margin/Content/ControllerRow"] -custom_minimum_size = Vector2(190, 0) +[node name="Label" type="Label" parent="Margin/Scroll/Content/ControllerRow"] +custom_minimum_size = Vector2(260, 0) layout_mode = 2 text = "controller camera sensitivity" +autowrap_mode = 2 -[node name="ControllerSensitivity" type="HSlider" parent="Margin/Content/ControllerRow"] +[node name="ControllerSensitivity" type="HSlider" parent="Margin/Scroll/Content/ControllerRow"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -101,36 +110,86 @@ max_value = 5.0 step = 0.1 value = 2.5 -[node name="ControllerSensitivityValue" type="Label" parent="Margin/Content/ControllerRow"] +[node name="ControllerSensitivityValue" type="Label" parent="Margin/Scroll/Content/ControllerRow"] unique_name_in_owner = true -custom_minimum_size = Vector2(62, 0) +custom_minimum_size = Vector2(100, 0) layout_mode = 2 text = "2.5" -[node name="InvertYToggle" type="CheckButton" parent="Margin/Content"] +[node name="InvertYToggle" type="CheckButton" parent="Margin/Scroll/Content"] unique_name_in_owner = true layout_mode = 2 text = "invert vertical camera" -[node name="SettingsFeedback" type="Label" parent="Margin/Content"] +[node name="WorldPixelRow" type="HBoxContainer" parent="Margin/Scroll/Content"] +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="Label" type="Label" parent="Margin/Scroll/Content/WorldPixelRow"] +custom_minimum_size = Vector2(260, 0) +layout_mode = 2 +text = "world pixelation" +autowrap_mode = 2 + +[node name="WorldPixelSize" type="HSlider" parent="Margin/Scroll/Content/WorldPixelRow"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 28) +layout_mode = 2 +size_flags_horizontal = 3 +min_value = 1.0 +max_value = 5.0 +step = 1.0 +value = 3.0 + +[node name="WorldPixelSizeValue" type="Label" parent="Margin/Scroll/Content/WorldPixelRow"] +unique_name_in_owner = true +custom_minimum_size = Vector2(170, 0) +layout_mode = 2 +text = "retro" + +[node name="UIPixelRow" type="HBoxContainer" parent="Margin/Scroll/Content"] +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="Label" type="Label" parent="Margin/Scroll/Content/UIPixelRow"] +custom_minimum_size = Vector2(260, 0) +layout_mode = 2 +text = "ui pixelation" +autowrap_mode = 2 + +[node name="UIPixelSize" type="HSlider" parent="Margin/Scroll/Content/UIPixelRow"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +min_value = 1.0 +max_value = 5.0 +step = 1.0 +value = 3.0 + +[node name="UIPixelSizeValue" type="Label" parent="Margin/Scroll/Content/UIPixelRow"] +unique_name_in_owner = true +custom_minimum_size = Vector2(170, 0) +layout_mode = 2 +text = "retro" + +[node name="SettingsFeedback" type="Label" parent="Margin/Scroll/Content"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 44) layout_mode = 2 horizontal_alignment = 1 -[node name="Buttons" type="HBoxContainer" parent="Margin/Content"] +[node name="Buttons" type="HBoxContainer" parent="Margin/Scroll/Content"] layout_mode = 2 theme_override_constants/separation = 12 alignment = 2 -[node name="ApplySettingsButton" type="Button" parent="Margin/Content/Buttons"] +[node name="ApplySettingsButton" type="Button" parent="Margin/Scroll/Content/Buttons"] unique_name_in_owner = true -custom_minimum_size = Vector2(120, 42) +custom_minimum_size = Vector2(150, 64) layout_mode = 2 text = "apply" -[node name="CancelSettingsButton" type="Button" parent="Margin/Content/Buttons"] +[node name="CancelSettingsButton" type="Button" parent="Margin/Scroll/Content/Buttons"] unique_name_in_owner = true -custom_minimum_size = Vector2(120, 42) +custom_minimum_size = Vector2(150, 64) layout_mode = 2 text = "back" diff --git a/ui/title_screen.gd b/ui/title_screen.gd index 2c70655..74a0552 100644 --- a/ui/title_screen.gd +++ b/ui/title_screen.gd @@ -73,6 +73,7 @@ enum ConfirmationAction { @onready var _confirmation_text: Label = %ConfirmationText @onready var _confirm_button: Button = %ConfirmButton @onready var _settings_panel: SettingsPanelType = %SettingsPanel +@onready var _background: ColorRect = %Background @onready var _decorative_fish_layer: Control = %DecorativeFishLayer @onready var _decorative_fish_timer: Timer = %DecorativeFishTimer @onready var _decorative_bubble_layer: Control = %DecorativeBubbleLayer @@ -102,6 +103,7 @@ var _awaiting_start_input: bool = false var _start_prompt_elapsed: float = 0.0 var _navigation_focus_active: bool = false var _modal_restore_navigation_focus: bool = false +var _world_pixel_size: int = PlayerSettings.DEFAULT_WORLD_PIXEL_SIZE func _ready() -> void: @@ -201,6 +203,54 @@ func _update_title_layout() -> void: minf(BUTTON_COLUMN_WIDTH, available_width), 0.0 ) + _update_world_preview_resolution() + + +func set_world_pixelation(pixel_size: int) -> void: + _world_pixel_size = clampi( + pixel_size, + PlayerSettings.MIN_WORLD_PIXEL_SIZE, + PlayerSettings.MAX_WORLD_PIXEL_SIZE + ) + if is_node_ready(): + _update_world_preview_resolution() + + +func _update_world_preview_resolution() -> void: + var render_scale: float = PlayerSettings.get_world_render_scale( + _world_pixel_size + ) + var shader_material := _background.material as ShaderMaterial + if shader_material != null: + shader_material.set_shader_parameter( + "virtual_pixel_density", + Vector2( + maxf(1.0, size.x * render_scale), + maxf(1.0, size.y * render_scale) + ) + ) + var logo_material := _title_logo.material as ShaderMaterial + if logo_material != null: + var effect_scale: float = 1.0 / render_scale + logo_material.set_shader_parameter( + "horizontal_displacement_pixels", + minf(2.0, 1.5 * effect_scale) + ) + logo_material.set_shader_parameter( + "bob_amount_pixels", + minf(4.0, 3.0 * effect_scale) + ) + + +func _snap_world_preview(value: Vector2) -> Vector2: + var render_scale: float = PlayerSettings.get_world_render_scale( + _world_pixel_size + ) + var step: float = 1.0 / render_scale + return Vector2( + roundf(value.x / step) * step, + roundf(value.y / step) * step + ) func _process(delta: float) -> void: @@ -742,6 +792,7 @@ func _update_decorative_fish( 8.0, maxf(8.0, _decorative_fish_layer.size.y - fish_control.size.y - 8.0) ) + fish_control.position = _snap_world_preview(fish_control.position) func _on_decorative_fish_finished( @@ -944,13 +995,13 @@ func _update_decorative_bubble( var wobble: float = sin( wobble_phase + progress * TAU * wobble_cycles ) * wobble_amplitude - bubble.position = Vector2( + bubble.position = _snap_world_preview(Vector2( base_x + horizontal_drift * progress + wobble - bubble.size.x * 0.5, lerpf(start_y, end_y, progress) - ) + )) func _on_decorative_bubble_finished( diff --git a/ui/title_screen.tscn b/ui/title_screen.tscn index 6919405..6df2b94 100644 --- a/ui/title_screen.tscn +++ b/ui/title_screen.tscn @@ -27,6 +27,7 @@ theme = ExtResource("2_theme") script = ExtResource("1_script") [node name="Background" type="ColorRect" parent="."] +unique_name_in_owner = true material = SubResource("ShaderMaterial_title_water") layout_mode = 1 anchors_preset = 15 @@ -92,7 +93,7 @@ mouse_filter = 2 theme_override_colors/font_color = Color(1, 1, 1, 1) theme_override_colors/font_outline_color = Color(0.02, 0.075, 0.11, 0.9) theme_override_constants/outline_size = 3 -theme_override_font_sizes/font_size = 26 +theme_override_font_sizes/font_size = 39 text = "press any key to start" horizontal_alignment = 1 vertical_alignment = 1 @@ -194,10 +195,10 @@ anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -offset_left = -230.0 -offset_top = -95.0 -offset_right = 230.0 -offset_bottom = 95.0 +offset_left = -320.0 +offset_top = -150.0 +offset_right = 320.0 +offset_bottom = 150.0 grow_horizontal = 2 grow_vertical = 2 @@ -226,13 +227,13 @@ alignment = 1 [node name="ConfirmButton" type="Button" parent="ConfirmationPanel/Margin/Content/Buttons"] unique_name_in_owner = true -custom_minimum_size = Vector2(150, 42) +custom_minimum_size = Vector2(190, 64) layout_mode = 2 text = "confirm" [node name="CancelConfirmButton" type="Button" parent="ConfirmationPanel/Margin/Content/Buttons"] unique_name_in_owner = true -custom_minimum_size = Vector2(120, 42) +custom_minimum_size = Vector2(160, 64) layout_mode = 2 text = "cancel" @@ -245,9 +246,9 @@ anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -offset_left = -300.0 -offset_top = -245.0 -offset_right = 300.0 -offset_bottom = 245.0 +offset_left = -360.0 +offset_top = -300.0 +offset_right = 360.0 +offset_bottom = 300.0 grow_horizontal = 2 grow_vertical = 2 diff --git a/ui/ui_pixelation_presenter.gd b/ui/ui_pixelation_presenter.gd new file mode 100644 index 0000000..ca41c15 --- /dev/null +++ b/ui/ui_pixelation_presenter.gd @@ -0,0 +1,111 @@ +class_name UIPixelationPresenter +extends SubViewportContainer + +const MIN_UI_VIEWPORT_SIZE: Vector2i = Vector2i(320, 180) + +signal effective_pixel_size_changed( + requested_pixel_size: int, + effective_pixel_size: int, +) + +@onready var _ui_viewport: SubViewport = $UIViewport +@onready var _ui_root: Control = $UIViewport/GameUI/UIRoot + +var _requested_pixel_size: int = PlayerSettings.DEFAULT_UI_PIXEL_SIZE +var _effective_pixel_size: int = PlayerSettings.DEFAULT_UI_PIXEL_SIZE +var _gameplay_active: bool = false +var _interactive_ui_open: bool = false + + +func _ready() -> void: + var root_viewport: Viewport = get_viewport() + root_viewport.size_changed.connect(_resize_presentation) + _resize_presentation() + + +func set_pixel_size(pixel_size: int) -> void: + _requested_pixel_size = clampi( + pixel_size, + PlayerSettings.MIN_UI_PIXEL_SIZE, + PlayerSettings.MAX_UI_PIXEL_SIZE + ) + _resize_presentation() + + +func get_requested_pixel_size() -> int: + return _requested_pixel_size + + +func get_effective_pixel_size() -> int: + return _effective_pixel_size + + +func get_ui_viewport_size() -> Vector2i: + return _ui_viewport.size + + +func set_gameplay_active(active: bool) -> void: + _gameplay_active = active + _refresh_mouse_filter() + + +func set_interactive_ui_open(is_open: bool) -> void: + _interactive_ui_open = is_open + _refresh_mouse_filter() + + +func _refresh_mouse_filter() -> void: + mouse_filter = ( + Control.MOUSE_FILTER_STOP + if not _gameplay_active or _interactive_ui_open + else Control.MOUSE_FILTER_IGNORE + ) + + +func _resize_presentation() -> void: + if not is_node_ready(): + return + var root_size: Vector2i = get_window().size + if root_size.x <= 0 or root_size.y <= 0: + return + var previous_effective_size: int = _effective_pixel_size + _effective_pixel_size = _resolve_effective_pixel_size(root_size) + var render_scale: float = PlayerSettings.get_ui_render_scale( + _effective_pixel_size + ) + var viewport_size := Vector2i( + ceili(float(root_size.x) * render_scale), + ceili(float(root_size.y) * render_scale) + ) + var presentation_size: Vector2 = Vector2(viewport_size) / render_scale + set_anchors_preset(Control.PRESET_TOP_LEFT) + position = (Vector2(root_size) - presentation_size) * 0.5 + size = Vector2(viewport_size) + scale = Vector2.ONE / render_scale + stretch_shrink = 1 + _ui_root.position = Vector2.ZERO + _ui_root.scale = Vector2.ONE * render_scale + _ui_root.size = Vector2(root_size) + if ( + previous_effective_size != _effective_pixel_size + or _requested_pixel_size != _effective_pixel_size + ): + effective_pixel_size_changed.emit( + _requested_pixel_size, + _effective_pixel_size + ) + + +func _resolve_effective_pixel_size(root_size: Vector2i) -> int: + for candidate: int in range(_requested_pixel_size, 0, -1): + var render_scale: float = PlayerSettings.get_ui_render_scale(candidate) + var candidate_size := Vector2i( + ceili(float(root_size.x) * render_scale), + ceili(float(root_size.y) * render_scale) + ) + if ( + candidate_size.x >= MIN_UI_VIEWPORT_SIZE.x + and candidate_size.y >= MIN_UI_VIEWPORT_SIZE.y + ): + return candidate + return PlayerSettings.MIN_UI_PIXEL_SIZE diff --git a/ui/ui_pixelation_presenter.gd.uid b/ui/ui_pixelation_presenter.gd.uid new file mode 100644 index 0000000..1b390d6 --- /dev/null +++ b/ui/ui_pixelation_presenter.gd.uid @@ -0,0 +1 @@ +uid://bdtb05vkiicd