diff --git a/export_presets.cfg b/export_presets.cfg index 347829e..2365877 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -9,7 +9,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="builds/*,playtest/*,scripts/*" -export_path="builds/v0.6.7-alpha/windows-x86_64/NETfishing.exe" +export_path="builds/v0.6.5-alpha/windows-x86_64/NETfishing.exe" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -35,11 +35,11 @@ application/modify_resources=true application/icon="res://art/exported/system_icons/netfishing.ico" application/console_wrapper_icon="" application/icon_interpolation=4 -application/file_version="0.6.7.0" -application/product_version="0.6.7.0" +application/file_version="0.6.5.0" +application/product_version="0.6.5.0" application/company_name="" application/product_name="NETfishing" -application/file_description="NETfishing v0.6.7-alpha" +application/file_description="NETfishing v0.6.5-alpha" application/copyright="" application/trademarks="" application/export_angle=0 @@ -62,7 +62,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="builds/*,playtest/*,scripts/*" -export_path="builds/v0.6.7-alpha/linux-arm64/NETfishing.arm64" +export_path="builds/v0.6.5-alpha/linux-arm64/NETfishing.arm64" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -92,7 +92,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="builds/*,playtest/*,scripts/*" -export_path="builds/v0.6.7-alpha/macos/NETfishing.zip" +export_path="builds/v0.6.5-alpha/macos/NETfishing.zip" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -108,8 +108,8 @@ custom_template/release="" application/bundle_identifier="io.woofmeow.netfishing" application/icon="res://art/exported/system_icons/netfishing_1024.png" application/icon_interpolation=0 -application/short_version="0.6.7" -application/version="0.6.7" +application/short_version="0.6.4" +application/version="0.6.4" application/architecture="universal" codesign/enable=false notarization/enable=false @@ -125,7 +125,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="builds/*,playtest/*,scripts/*,tests/*" -export_path="builds/v0.6.7-alpha/server-linux-x86_64/NETfishingServer.x86_64" +export_path="builds/v0.6.5-alpha/server-linux-x86_64/NETfishingServer.x86_64" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -155,7 +155,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="builds/*,playtest/*,scripts/*" -export_path="builds/v0.6.7-alpha/android/NETfishing.apk" +export_path="builds/v0.6.5-alpha/android/NETfishing.apk" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -176,8 +176,8 @@ architectures/armeabi-v7a=false architectures/arm64-v8a=true architectures/x86=false architectures/x86_64=false -version/code=60700 -version/name="v0.6.7-alpha" +version/code=60500 +version/name="v0.6.5-alpha" package/unique_name="io.woofmeow.netfishing" package/name="NETfishing" package/signed=true @@ -214,7 +214,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="builds/*,playtest/*,scripts/*" -export_path="builds/v0.6.7-alpha/linux-x86_64/NETfishing.x86_64" +export_path="builds/v0.6.5-alpha/linux-x86_64/NETfishing.x86_64" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" diff --git a/fishing/fishing_presentation.gd b/fishing/fishing_presentation.gd index 71fedac..c38ddd6 100644 --- a/fishing/fishing_presentation.gd +++ b/fishing/fishing_presentation.gd @@ -431,7 +431,7 @@ func _draw_line_ribbon(points: PackedVector3Array) -> void: return var half_thickness: float = line_thickness * 0.5 - var ribbon_vertices := PackedVector3Array() + _line_mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLES) for point_index: int in range(1, points.size()): var start: Vector3 = points[point_index - 1] var end: Vector3 = points[point_index] @@ -452,17 +452,12 @@ func _draw_line_ribbon(points: PackedVector3Array) -> void: var start_right: Vector3 = to_local(start + side) var end_right: Vector3 = to_local(end + side) var end_left: Vector3 = to_local(end - side) - ribbon_vertices.append(start_left) - ribbon_vertices.append(start_right) - ribbon_vertices.append(end_right) - ribbon_vertices.append(start_left) - ribbon_vertices.append(end_right) - ribbon_vertices.append(end_left) - if ribbon_vertices.is_empty(): - return - _line_mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLES) - for vertex: Vector3 in ribbon_vertices: - _line_mesh.surface_add_vertex(vertex) + _line_mesh.surface_add_vertex(start_left) + _line_mesh.surface_add_vertex(start_right) + _line_mesh.surface_add_vertex(end_right) + _line_mesh.surface_add_vertex(start_left) + _line_mesh.surface_add_vertex(end_right) + _line_mesh.surface_add_vertex(end_left) _line_mesh.surface_end() diff --git a/network/discovery_client.gd b/network/discovery_client.gd index bdb7134..f2263e8 100644 --- a/network/discovery_client.gd +++ b/network/discovery_client.gd @@ -865,46 +865,12 @@ func _parse_response_dictionary(body: PackedByteArray) -> Dictionary: func _request_failure(response: Dictionary) -> String: var error: Variant = response.get("error", {}) if typeof(error) == TYPE_DICTIONARY: - var details := error as Dictionary - if str(details.get("code", "")) == "game_version_mismatch": - var required_version: String = str( - details.get("required_game_version", "") - ).strip_edges() - if not required_version.is_empty(): - return _discovery_version_mismatch_message(required_version) - var message: String = str(details.get("message", "")).strip_edges() + var message: String = str((error as Dictionary).get("message", "")).strip_edges() if not message.is_empty(): return message return "Room discovery is temporarily unavailable." -func _discovery_version_mismatch_message(required_version: String) -> String: - var local_version: String = NetworkProtocol.game_version() - var rejection: NetworkProtocol.RejectionCode = ( - NetworkProtocol.game_version_rejection( - local_version, - required_version, - ) - ) - match rejection: - NetworkProtocol.RejectionCode.CLIENT_OUTDATED: - return ( - "Your NETfishing version is out of date. Update to %s to " - + "enable public discovery. This room will not be listed " - + "until you update." - ) % required_version - NetworkProtocol.RejectionCode.SERVER_OUTDATED: - return ( - "Public discovery is still on NETfishing %s. This room " - + "will not be listed until discovery is updated for %s." - ) % [required_version, local_version] - _: - return ( - "Public discovery requires NETfishing %s. This room will " - + "not be listed until both versions match." - ) % required_version - - func _on_session_state_changed(state: NetworkSession.State) -> void: if state == NetworkSession.State.CONNECTING and not _pending_join_token.is_empty(): _set_public_join_state(PublicJoinState.CONNECTING) diff --git a/playtest/README-PLAYTEST.txt b/playtest/README-PLAYTEST.txt index 258ea6a..47e0a86 100644 --- a/playtest/README-PLAYTEST.txt +++ b/playtest/README-PLAYTEST.txt @@ -1,13 +1,12 @@ NETfishing -v0.6.7-alpha -Alpha 0.6.7 +v0.6.5-alpha +Alpha 0.6.5 Thank you for trying this early private playtest. -NETfishing is an early multiplayer fishing game. This alpha includes local -progression, player-hosted and dedicated multiplayer sessions, public room -discovery, and direct UDP connections. Expect rough edges and please keep a -backup of any save or identity data you care about. +NETfishing is currently a local gameplay prototype for a future +multiplayer-first game. Networking and multiplayer are NOT implemented in +this build. INSTALLATION @@ -61,9 +60,6 @@ FISHING FEATURES TO TRY -- Hosting a private or public room and joining compatible online sessions -- Direct-IP, saved-server, recent-server, and public-room connection flows -- Text chat, player calls, player lists, relationships, and moderation tools - Movement, jumping, camera rotation, and zoom - Short, medium, and maximum-distance casts - Valid water and invalid land casts @@ -105,11 +101,11 @@ Please include: - How movement, casting, chase pressure, barrier clicking, menus, and camera controls felt -KNOWN ALPHA LIMITATIONS +KNOWN PRE-ALPHA LIMITATIONS -- Public sessions remain direct connections; restrictive NAT or carrier-grade - networks may prevent some players from joining. -- Multiplayer, dedicated-server deployment, UI, and balance are still alpha. -- Some fish and character artwork remains temporary. +- Multiplayer/networking is not implemented. +- Fish and character art are temporary. +- UI and balance values are placeholders. +- There is no audio. - There is no installer or automatic updater. - Windows builds are unsigned. diff --git a/project.godot b/project.godot index 27fe914..ac103f3 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="NETFISHING" -config/version="0.6.7-alpha" +config/version="0.6.5-alpha" run/main_scene="res://main/main.tscn" config/features=PackedStringArray("4.7", "GL Compatibility") config/icon="res://art/exported/system_icons/netfishing_256.png" diff --git a/scripts/build_playtest.sh b/scripts/build_playtest.sh index 3a47661..7ddf4f9 100755 --- a/scripts/build_playtest.sh +++ b/scripts/build_playtest.sh @@ -4,12 +4,12 @@ set -euo pipefail readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" readonly PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" -readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.6.7-alpha" +readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.6.5-alpha" readonly WINDOWS_DIR="${BUILD_ROOT}/windows-x86_64" readonly LINUX_DIR="${BUILD_ROOT}/linux-x86_64" readonly README_SOURCE="${PROJECT_ROOT}/playtest/README-PLAYTEST.txt" -readonly WINDOWS_ZIP="${BUILD_ROOT}/NETfishing-v0.6.7-alpha-windows-x86_64.zip" -readonly LINUX_ZIP="${BUILD_ROOT}/NETfishing-v0.6.7-alpha-linux-x86_64.zip" +readonly WINDOWS_ZIP="${BUILD_ROOT}/NETfishing-v0.6.5-alpha-windows-x86_64.zip" +readonly LINUX_ZIP="${BUILD_ROOT}/NETfishing-v0.6.5-alpha-linux-x86_64.zip" readonly GODOT_BIN="${GODOT_BIN:-godot}" if [[ ! -f "${PROJECT_ROOT}/project.godot" ]]; then diff --git a/tests/art_tools_validation.gd b/tests/art_tools_validation.gd index 1fa4dee..874cc4e 100644 --- a/tests/art_tools_validation.gd +++ b/tests/art_tools_validation.gd @@ -109,7 +109,6 @@ func _run() -> void: select_button.button_index = JOY_BUTTON_BACK select_button.pressed = true assert(bool(game_ui.call("_handle_controller_chat_controls", select_button))) - await process_frame assert(chat_ui.is_open()) assert(chat_panel.mouse_filter == Control.MOUSE_FILTER_STOP) var typed_chat_entry := chat_ui.find_child( @@ -122,8 +121,6 @@ func _run() -> void: accept_button.pressed = true assert(bool(game_ui.call("_handle_controller_chat_controls", accept_button))) assert(typed_chat_entry.virtual_keyboard_enabled) - await process_frame - assert(typed_chat_entry.has_focus()) var left_bumper := InputEventJoypadButton.new() left_bumper.button_index = JOY_BUTTON_LEFT_SHOULDER left_bumper.pressed = true @@ -132,8 +129,10 @@ func _run() -> void: assert(chat_panel.mouse_filter == Control.MOUSE_FILTER_IGNORE) assert(not typed_chat_entry.virtual_keyboard_enabled) assert(bool(game_ui.call("_handle_controller_chat_controls", left_bumper))) - assert(not chat_ui.is_open()) + assert(chat_ui.is_open()) assert(not typed_chat_entry.virtual_keyboard_enabled) + assert(bool(game_ui.call("_handle_controller_chat_controls", left_bumper))) + assert(not chat_ui.is_open()) assert(bool(game_ui.call("_handle_controller_chat_controls", select_button))) assert(not chat_ui.is_open()) assert(chat_ui.is_collapsed()) @@ -316,7 +315,5 @@ func _run() -> void: var session := main.get_node("%NetworkSession") as NetworkSession session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() diff --git a/tests/controller_focus_presentation_validation.gd b/tests/controller_focus_presentation_validation.gd index f0d0355..638e0cc 100644 --- a/tests/controller_focus_presentation_validation.gd +++ b/tests/controller_focus_presentation_validation.gd @@ -39,15 +39,6 @@ func _run() -> void: standard_button.material != null, "ordinary controller focus receives inversion", ) - var pointer_motion := InputEventMouseMotion.new() - pointer_motion.position = Vector2(4.0, 4.0) - presentation._input(pointer_motion) - await process_frame - _expect( - standard_button.material == null, - "mouse motion clears controller focus presentation", - ) - presentation._input(controller_event) authored_selector.grab_focus() await process_frame diff --git a/tests/controller_focus_recovery_validation.gd b/tests/controller_focus_recovery_validation.gd index f598511..e34afaa 100644 --- a/tests/controller_focus_recovery_validation.gd +++ b/tests/controller_focus_recovery_validation.gd @@ -23,45 +23,14 @@ func _run() -> void: var original := _make_button("cute", "cute (show variants)") option_list.add_child(original) await process_frame - original.grab_focus() - await process_frame - await process_frame - _expect( - root.gui_get_focus_owner() == null, - "programmatic focus stays pending outside navigation mode", - ) - var keyboard_navigation := InputEventKey.new() - keyboard_navigation.keycode = KEY_DOWN - keyboard_navigation.pressed = true - recovery._input(keyboard_navigation) - await process_frame - await process_frame - _expect( - root.gui_get_focus_owner() == original, - "keyboard navigation restores pending initial focus", - ) - var menu_shortcut := InputEventKey.new() - menu_shortcut.physical_keycode = KEY_TAB - menu_shortcut.pressed = true - recovery._input(menu_shortcut) - await process_frame - await process_frame - _expect( - root.gui_get_focus_owner() == null, - "the menu shortcut does not leave a pseudo-focused option", - ) var controller_event := InputEventJoypadButton.new() controller_event.button_index = JOY_BUTTON_A controller_event.pressed = true recovery._input(controller_event) + original.grab_focus() await process_frame - await process_frame - await process_frame - _expect( - root.gui_get_focus_owner() == original, - "controller navigation restores pending initial focus", - ) + _expect(root.gui_get_focus_owner() == original, "original option receives focus") option_list.remove_child(original) original.queue_free() @@ -84,63 +53,6 @@ func _run() -> void: root.gui_get_focus_owner() == explicit_target, "explicit focus changes take precedence over recovery", ) - var pointer_motion := InputEventMouseMotion.new() - pointer_motion.position = Vector2(4.0, 4.0) - recovery._input(pointer_motion) - await process_frame - await process_frame - _expect( - root.gui_get_focus_owner() == null, - "mouse motion releases controller focus", - ) - await process_frame - _expect( - root.gui_get_focus_owner() == null, - "mouse motion prevents automatic focus recovery", - ) - recovery._input(controller_event) - await process_frame - await process_frame - _expect( - root.gui_get_focus_owner() == explicit_target, - "controller navigation can resume from pointer mode", - ) - var pointer_press := InputEventMouseButton.new() - pointer_press.button_index = MOUSE_BUTTON_LEFT - pointer_press.pressed = true - recovery._input(pointer_press) - await process_frame - _expect( - root.gui_get_focus_owner() == explicit_target, - "mouse-down preserves focus through button activation", - ) - var pointer_release := InputEventMouseButton.new() - pointer_release.button_index = MOUSE_BUTTON_LEFT - pointer_release.pressed = false - recovery._input(pointer_release) - await process_frame - await process_frame - _expect( - root.gui_get_focus_owner() == null, - "mouse-up clears the completed button focus", - ) - recovery._input(controller_event) - await process_frame - await process_frame - - var outside_button := _make_button("outside", "outside") - stage.add_child(outside_button) - option_list.hide() - root.gui_release_focus() - await process_frame - await process_frame - _expect( - root.gui_get_focus_owner() == null, - "focus recovery never escapes a hidden menu scope", - ) - option_list.show() - explicit_target.grab_focus() - await process_frame var leave_world_ui_event := InputEventJoypadButton.new() leave_world_ui_event.button_index = JOY_BUTTON_LEFT_SHOULDER leave_world_ui_event.pressed = true @@ -152,14 +64,6 @@ func _run() -> void: root.gui_get_focus_owner() == null, "LB intentionally leaving world UI is never recovered", ) - var accessibility_only := Control.new() - accessibility_only.focus_mode = Control.FOCUS_ACCESSIBILITY - stage.add_child(accessibility_only) - await process_frame - _expect( - not bool(recovery.call("_is_focusable", accessibility_only)), - "accessibility-only controls are not recovery targets", - ) stage.queue_free() if _failures.is_empty(): diff --git a/tests/dedicated_server_config_validation.gd b/tests/dedicated_server_config_validation.gd index ecc721c..7911f6d 100644 --- a/tests/dedicated_server_config_validation.gd +++ b/tests/dedicated_server_config_validation.gd @@ -107,46 +107,6 @@ func _run() -> void: "0.6.5-beta", "0.6.5-alpha" ) == NetworkProtocol.RejectionCode.SERVER_OUTDATED ) - var outdated_discovery_error := { - "error": { - "code": "game_version_mismatch", - "message": "generic mismatch", - "required_game_version": "0.6.8-alpha", - }, - } - var outdated_message: String = str( - discovery.call("_request_failure", outdated_discovery_error) - ) - assert("out of date" in outdated_message) - assert("0.6.8-alpha" in outdated_message) - assert("will not be listed" in outdated_message) - discovery.set( - "_host_request_kind", - DiscoveryClient.HostRequestKind.CREATE, - ) - discovery.set("_host_request_in_flight", true) - discovery.call( - "_on_host_request_completed", - HTTPRequest.RESULT_SUCCESS, - HTTPClient.RESPONSE_CONFLICT, - PackedStringArray(), - JSON.stringify(outdated_discovery_error).to_utf8_buffer(), - ) - assert(discovery.get_host_state() == DiscoveryClient.HostState.ERROR) - assert(discovery.host_status_is_error()) - assert(discovery.get_host_status_message() == outdated_message) - var newer_discovery_error := { - "error": { - "code": "game_version_mismatch", - "message": "generic mismatch", - "required_game_version": "0.6.6-alpha", - }, - } - var newer_message: String = str( - discovery.call("_request_failure", newer_discovery_error) - ) - assert("discovery is updated" in newer_message) - assert("will not be listed" in newer_message) discovery.free() print("DEDICATED_SERVER_CONFIG_VALIDATION_OK") quit() diff --git a/tests/economy_regression_validation.gd b/tests/economy_regression_validation.gd index f0046fa..f7d0d12 100644 --- a/tests/economy_regression_validation.gd +++ b/tests/economy_regression_validation.gd @@ -82,8 +82,7 @@ func _run() -> void: print("Economy regression validation: PASS") session.disconnect_session("Economy validation complete.") main.queue_free() - for _frame: int in 4: - await process_frame + await process_frame quit() @@ -127,9 +126,7 @@ func _run_multiplayer_host() -> void: print("Economy multiplayer host validation: PASS") session.disconnect_session("Economy host validation complete.") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() @@ -231,9 +228,7 @@ func _run_multiplayer_client() -> void: print("Economy multiplayer client validation: PASS") session.disconnect_session("Economy client validation complete.") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() @@ -412,7 +407,7 @@ func _test_player_menu_sale( assert(sell_action.mouse_filter == Control.MOUSE_FILTER_STOP) assert(sell_action.focus_mode == Control.FOCUS_ALL) assert(ui_viewport != null) - await _activate_pointer_control(sell_action, ui_viewport) + await _activate_focused_button(sell_action, ui_viewport) await process_frame assert(confirmation.visible) assert(confirm_button.visible and not confirm_button.disabled) @@ -421,7 +416,7 @@ func _test_player_menu_sale( > (player_menu.get_node("%CoolerOuterWall") as Control).z_index ) _sale_result.clear() - await _activate_pointer_control(confirm_button, ui_viewport) + await _activate_focused_button(confirm_button, ui_viewport) await process_frame assert(not _sale_result.is_empty() and bool(_sale_result[1])) assert(not player.inventory.contains_catch_id(fish_catch.catch_id)) @@ -440,29 +435,18 @@ func _test_player_menu_sale( await create_timer(2.2).timeout -func _activate_pointer_control( - control: Control, +func _activate_focused_button( + button: Button, ui_viewport: SubViewport, ) -> void: - var presenter := ui_viewport.get_parent() as SubViewportContainer - assert(presenter != null) - var local_center: Vector2 = control.get_global_transform_with_canvas() * ( - control.size * 0.5 - ) - var center: Vector2 = presenter.position + local_center * presenter.scale - var motion := InputEventMouseMotion.new() - motion.position = center - motion.global_position = center - root.push_input(motion, true) + button.grab_focus() await process_frame - assert(ui_viewport.gui_get_hovered_control() == control) + assert(button.has_focus()) for is_pressed: bool in [true, false]: - var click := InputEventMouseButton.new() - click.button_index = MOUSE_BUTTON_LEFT - click.position = center - click.global_position = center - click.pressed = is_pressed - root.push_input(click, true) + var accept := InputEventAction.new() + accept.action = &"ui_accept" + accept.pressed = is_pressed + ui_viewport.push_input(accept, false) await process_frame @@ -571,25 +555,19 @@ func _test_fishing_shop_sale_ui( assert(not shop.has_node("ShopPanel/Margin/Layout/ModeTabs")) var shop_tabs: Array = shop.get("_shop_tabs") as Array assert(shop_tabs.size() == 6) - var art_supplies_tab := shop_tabs[4] as Button - assert(art_supplies_tab != null and art_supplies_tab.text == "Art Supplies") var sell_mode := shop_tabs[5] as Button assert(sell_mode != null and sell_mode.text == "Sell Fish") - await _activate_pointer_control(art_supplies_tab, ui_viewport) - await process_frame var stock_sections: Array[String] = [] for child: Node in shop.get_node("%SuppliesList").get_children(): if child is Label: stock_sections.append((child as Label).text) - assert(stock_sections == ["art kit", "markers", "brushes", "grids"]) - await _activate_pointer_control(sell_mode, ui_viewport) + assert(stock_sections == ["supplies", "art kit", "markers", "brushes", "grids"]) + await _activate_focused_button(sell_mode, ui_viewport) await process_frame assert(shop.visible and not player_menu.visible) assert(not shop.has_node("ShopPanel/Margin/Layout/Body/FishSales")) assert((shop.get_node("%ShopCoolerPage") as Control).visible) - assert((shop.get_node("%ShopPanel") as Control).visible) - assert(not (shop.get_node("ShopPanel/Margin/Layout/Body") as Control).visible) - assert(not (shop.get_node("%Feedback") as Control).visible) + assert(not (shop.get_node("%ShopPanel") as Control).visible) var mounted_cooler := player_menu.get("_cooler_page") as Control assert(mounted_cooler != null and mounted_cooler.visible) assert( @@ -599,11 +577,6 @@ func _test_fishing_shop_sale_ui( var water_surface := player_menu.get("_cooler_water_surface") as ColorRect assert(cooler_outer_wall != null and cooler_outer_wall.visible) assert(water_surface.visible and water_surface.material is ShaderMaterial) - var cooler_sort_option := player_menu.get("_cooler_sort_option") as Control - await _activate_pointer_control(cooler_sort_option, ui_viewport) - var cooler_choice_panel := cooler_sort_option.get("_choice_panel") as Control - assert(cooler_choice_panel.visible) - cooler_sort_option.call("close_choices") assert( StringName( (player_menu.get("_sale_buyer_override") as FishBuyerProfile).id @@ -614,10 +587,10 @@ func _test_fishing_shop_sale_ui( var reserved_button := fish_nodes.get(reserved_catch.catch_id) as Button assert(fish_button != null and fish_button.visible) assert(reserved_button != null and reserved_button.visible) - await _activate_pointer_control(fish_button, ui_viewport) + await _activate_focused_button(fish_button, ui_viewport) var sell_button := player_menu.get("_sell_bubble") as Button assert(sell_button.visible and not sell_button.disabled) - await _activate_pointer_control(sell_button, ui_viewport) + await _activate_focused_button(sell_button, ui_viewport) await process_frame var confirmation := player_menu.get("_sale_confirmation") as Control var confirm_button := player_menu.get("_confirm_sale_button") as Button @@ -627,21 +600,20 @@ func _test_fishing_shop_sale_ui( > cooler_outer_wall.z_index ) _sale_result.clear() - await _activate_pointer_control(confirm_button, ui_viewport) + await _activate_focused_button(confirm_button, ui_viewport) await process_frame assert(not _sale_result.is_empty() and bool(_sale_result[1])) assert(not player.inventory.contains_catch_id(fish_catch.catch_id)) assert(player.wallet.get_balance() == balance_before + fish_catch.sale_value) assert(not sale_service.is_local_sale_pending()) assert(reservations.release(reservation_id)) - await _activate_pointer_control(shop_tabs[0] as Button, ui_viewport) + await _activate_focused_button(shop_tabs[0] as Button, ui_viewport) await process_frame assert(shop.visible and (shop.get_node("%ShopPanel") as Control).visible) assert(not (shop.get_node("%ShopCoolerPage") as Control).visible) assert(not player_menu.is_shop_cooler_mounted()) shop.close_shop() - await shop.menu_visibility_changed - assert(not shop.visible) + await process_frame func _assert_sale( @@ -662,9 +634,7 @@ func _assert_sale( assert(bool(_sale_result[1]) == expected_success) assert(not sale_service.is_local_sale_pending()) if expected_success: - var payout: int = int(_sale_result[4]) - assert(payout >= 0) - assert(player.wallet.get_balance() == balance_before + payout) + assert(player.wallet.get_balance() > balance_before) for catch_id: StringName in catch_ids: assert(not player.inventory.contains_catch_id(catch_id)) else: diff --git a/tests/fish_hotbar_showcase_validation.gd b/tests/fish_hotbar_showcase_validation.gd index 8919221..ec802b0 100644 --- a/tests/fish_hotbar_showcase_validation.gd +++ b/tests/fish_hotbar_showcase_validation.gd @@ -3,6 +3,7 @@ extends SceneTree const MainScene = preload("res://main/main.tscn") const FishCatchType = preload("res://fish/fish_catch.gd") const FishDataType = preload("res://fish/fish_data.gd") +const FishingSpotType = preload("res://fishing/fishing_spot.gd") func _initialize() -> void: @@ -39,6 +40,7 @@ func _run() -> void: var service := main.get_node( "%NetworkFishShowcaseService" ) as NetworkFishShowcaseService + var fishing_spot := main.get_node("%FishingSpot") as FishingSpotType assert(player != null and fish_catalog != null and service != null) var fish: FishDataType = fish_catalog.get_fish_by_id(&"bluegill") assert(fish != null) @@ -81,18 +83,22 @@ func _run() -> void: assert(player.hotbar.get_selected_fish_catch_id() == fish_catch.catch_id) assert(player.hotbar.get_selected_item_id().is_empty()) + var press := InputEventMouseButton.new() + press.button_index = MOUSE_BUTTON_LEFT + press.pressed = true + fishing_spot.call("_unhandled_input", press) + await process_frame assert(service.is_local_showcase_visible()) assert(service.get_local_showcase_catch_id() == fish_catch.catch_id) - await _wait_for_held_fish_visibility(player, true) - assert(service.toggle_selected_fish()) + assert((player.get_node("%HeldFishDisplay") as Node3D).visible) + fishing_spot.call("_unhandled_input", press) await process_frame assert(not service.is_local_showcase_visible()) - await _wait_for_held_fish_visibility(player, false) + assert(not (player.get_node("%HeldFishDisplay") as Node3D).visible) var saved_weather: WorldWeatherService.Weather = ( world_weather.get_weather() ) var saved_weather_seconds: float = world_weather.get_seconds_remaining() - var saved_time_hours: float = world_time.get_time_hours() assert(save_manager.save_now()) var save_path: String = str(save_manager.get("_save_path")) @@ -110,7 +116,7 @@ func _run() -> void: == 125 ) assert(absf( - float((parsed as Dictionary)["world"]["time_hours"]) - saved_time_hours + float((parsed as Dictionary)["world"]["time_hours"]) - 19.75 ) < 0.01) assert( int((parsed as Dictionary)["world"]["weather"]) @@ -143,7 +149,7 @@ func _run() -> void: ) assert(save_manager.load_player_data()) assert(player.experience.get_total_experience() == 125) - assert(absf(world_time.get_time_hours() - saved_time_hours) < 0.01) + assert(absf(world_time.get_time_hours() - 19.75) < 0.01) assert(world_weather.get_weather() == saved_weather) assert(absf( world_weather.get_seconds_remaining() - saved_weather_seconds @@ -157,13 +163,14 @@ func _run() -> void: FishQuality.Tier.EXCEPTIONAL, ) ) + assert(not service.is_local_showcase_visible()) + assert(service.toggle_selected_fish()) assert(service.is_local_showcase_visible()) - await _wait_for_held_fish_visibility(player, true) assert(player.inventory.remove_catch_by_id(fish_catch.catch_id) != null) await process_frame assert(player.hotbar.get_fish_catch_id(1).is_empty()) assert(not service.is_local_showcase_visible()) - await _wait_for_held_fish_visibility(player, false) + assert(not (player.get_node("%HeldFishDisplay") as Node3D).visible) var valid_state: Dictionary = { "session_id": "session", @@ -195,26 +202,5 @@ func _run() -> void: print("Fish hotbar showcase validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() - - -func _wait_for_held_fish_visibility( - player: Player, - expected_visible: bool, -) -> void: - var held_fish_display := player.get("_held_fish_display") as Node3D - assert(held_fish_display != null) - var deadline_msec: int = Time.get_ticks_msec() + 2000 - while ( - held_fish_display.visible != expected_visible - and Time.get_ticks_msec() < deadline_msec - ): - await process_frame - assert( - held_fish_display.visible == expected_visible, - "Held fish display did not become %s within the timeout." - % expected_visible, - ) diff --git a/tests/fish_showcase_multiplayer_validation.gd b/tests/fish_showcase_multiplayer_validation.gd index 72f4a59..23f02ef 100644 --- a/tests/fish_showcase_multiplayer_validation.gd +++ b/tests/fish_showcase_multiplayer_validation.gd @@ -35,8 +35,8 @@ func _run_host() -> void: var fish_catch: FishCatch = _add_bluegill(main, player) assert(player.hotbar.assign_fish(1, fish_catch.catch_id)) assert(player.hotbar.select_slot(1)) + assert(service.toggle_selected_fish()) assert(service.is_local_showcase_visible()) - await _wait_for_held_fish_visibility(player, true) assert(session.set_host_open(true)) var remote_peer_id: int = 0 @@ -66,9 +66,7 @@ func _run_host() -> void: print("Fish showcase multiplayer host validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() @@ -120,8 +118,8 @@ func _run_client() -> void: var fish_catch: FishCatch = _add_bluegill(main, player) assert(player.hotbar.assign_fish(1, fish_catch.catch_id)) assert(player.hotbar.select_slot(1)) - assert(service.is_local_showcase_visible()) - await _wait_for_held_fish_visibility(player, true) + assert(service.toggle_selected_fish()) + assert((player.get("_held_fish_display") as Node3D).visible) await create_timer(2.0).timeout assert(service.toggle_selected_fish()) var held_display := player.get("_held_fish_display") as Node3D @@ -134,9 +132,7 @@ func _run_client() -> void: print("Fish showcase multiplayer client validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() @@ -170,18 +166,3 @@ func _create_initialized_main() -> Node: await process_frame assert(bool(main.get("_application_initialized"))) return main - - -func _wait_for_held_fish_visibility( - player: Player, - expected_visible: bool, -) -> void: - var held_fish_display := player.get("_held_fish_display") as Node3D - assert(held_fish_display != null) - var deadline_msec: int = Time.get_ticks_msec() + 2000 - while ( - held_fish_display.visible != expected_visible - and Time.get_ticks_msec() < deadline_msec - ): - await process_frame - assert(held_fish_display.visible == expected_visible) diff --git a/tests/fishing_authority_validation.gd b/tests/fishing_authority_validation.gd index 52ab878..dc79c44 100644 --- a/tests/fishing_authority_validation.gd +++ b/tests/fishing_authority_validation.gd @@ -36,14 +36,8 @@ func _run() -> void: assert(not session.is_open_host()) assert(service != null and fishing_spot != null and player != null) assert(player.hotbar.get_selected_item_id() == &"basic_fishing_rod") - var pond := main.get_node( - "TestWorld/Regions/StarterIslandRegion/WaterBodies/Pond" - ) as Node3D - var pond_region := pond.get_node("FishingRegion") as FishableWaterRegion - assert(pond != null and pond_region != null) - var pond_surface_y: float = pond_region.get_surface_height() - player.global_position = pond.global_position + Vector3(8.9, 1.44, 0.0) + player.global_position = Vector3(-0.5, 3.95, 2.1) var visuals := player.get_node("Visuals") as Node3D visuals.rotation.y = PI * 0.5 for _frame: int in 4: @@ -56,8 +50,8 @@ func _run() -> void: fishing_spot.set("_cast_charge", 0.32) fishing_spot.call("_update_cast_charge", 0.0) var aimed_target: Vector3 = fishing_spot.get("_cast_target") - assert(aimed_target.x < player.global_position.x - 0.85) - assert(is_equal_approx(aimed_target.y, pond_surface_y)) + assert(aimed_target.x < -1.35) + assert(is_equal_approx(aimed_target.y, 2.51)) assert(fishing_spot.is_target_fishable(aimed_target)) fishing_spot.call("_confirm_cast") assert(fishing_spot.state == FishingSpotType.FishingState.CASTING) @@ -74,7 +68,7 @@ func _run() -> void: var attempts: Dictionary = service.get("_attempts") var attempt: NetworkFishingAttempt = attempts.get(session.get_local_peer_id()) assert(attempt != null) - assert(is_equal_approx(attempt.target.y, pond_surface_y)) + assert(is_equal_approx(attempt.target.y, 2.51)) assert(attempt.bobber_position.is_equal_approx(attempt.target)) fishing_spot.set("_withdrawal_input_held", true) @@ -130,42 +124,29 @@ func _run() -> void: fish, ) assert(fish_catch != null and fish_catch.is_valid()) - var reference_controller := CatchController.new() - root.add_child(reference_controller) - reference_controller.start_authoritative_encounter( + var baseline_controller := CatchController.new() + root.add_child(baseline_controller) + baseline_controller.start_authoritative_encounter( fish.catch_profile, attempt.reel_speed, attempt.barrier_damage, attempt.encounter_seed, - fish_catch.quality, - int(fish.rarity), - fish.get_weight_percentile(fish_catch.weight_lb), + FishQuality.Tier.BORING, ) var quality_barriers: Array = attempt.controller.get("_barriers") - var reference_barriers: Array = reference_controller.get("_barriers") - assert(quality_barriers.size() == reference_barriers.size()) + var baseline_barriers: Array = baseline_controller.get("_barriers") + assert(quality_barriers.size() == baseline_barriers.size()) for barrier_index: int in quality_barriers.size(): var quality_barrier: RefCounted = quality_barriers[barrier_index] - var reference_barrier: RefCounted = reference_barriers[barrier_index] + var baseline_barrier: RefCounted = baseline_barriers[barrier_index] assert( - is_equal_approx( - float(quality_barrier.get("position")), - float(reference_barrier.get("position")), + int(quality_barrier.get("maximum_health")) + == FishQuality.apply_barrier_health( + int(baseline_barrier.get("maximum_health")), + fish_catch.quality, ) ) - assert( - int(quality_barrier.get("maximum_health")) - == int(reference_barrier.get("maximum_health")) - ) - assert( - int(quality_barrier.get("maximum_health")) - >= FishQuality.BARRIER_HEALTH_MINIMUMS[fish_catch.quality] - ) - assert( - int(quality_barrier.get("maximum_health")) - <= FishQuality.BARRIER_HEALTH_MAXIMUMS[fish_catch.quality] - ) - reference_controller.queue_free() + baseline_controller.queue_free() service.call("_cancel_attempt", session.get_local_peer_id(), "") await process_frame assert(not service.has_local_attempt()) @@ -173,7 +154,5 @@ func _run() -> void: print("Fishing authority validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() diff --git a/tests/fishing_multiplayer_validation.gd b/tests/fishing_multiplayer_validation.gd index f6a8b40..2dae5e3 100644 --- a/tests/fishing_multiplayer_validation.gd +++ b/tests/fishing_multiplayer_validation.gd @@ -150,9 +150,7 @@ func _run_host() -> void: print("Fishing multiplayer host validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() @@ -327,9 +325,7 @@ func _run_client() -> void: print("Fishing multiplayer client validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() diff --git a/tests/fishing_surface_validation.gd b/tests/fishing_surface_validation.gd index 6ed3486..15cc428 100644 --- a/tests/fishing_surface_validation.gd +++ b/tests/fishing_surface_validation.gd @@ -266,47 +266,11 @@ func _validate_bite_wait_distribution() -> void: if wait_seconds >= FishingSpotType.BITE_LONG_MAX_SECONDS: very_long_count += 1 var average_wait_seconds: float = total_wait_seconds / 10000.0 - var quick_ratio: float = float(quick_count) / 10000.0 - var very_long_ratio: float = float(very_long_count) / 10000.0 - var very_long_probability: float = 1.0 - ( - FishingSpotType.BITE_QUICK_PROBABILITY - + FishingSpotType.BITE_TYPICAL_PROBABILITY - + FishingSpotType.BITE_LONG_PROBABILITY - ) - var expected_average_wait_seconds: float = ( - FishingSpotType.BITE_QUICK_PROBABILITY - * ( - FishingSpotType.BITE_QUICK_MIN_SECONDS - + FishingSpotType.BITE_QUICK_MAX_SECONDS - ) - * 0.5 - + FishingSpotType.BITE_TYPICAL_PROBABILITY - * ( - FishingSpotType.BITE_QUICK_MAX_SECONDS - + FishingSpotType.BITE_TYPICAL_MAX_SECONDS - ) - * 0.5 - + FishingSpotType.BITE_LONG_PROBABILITY - * ( - FishingSpotType.BITE_TYPICAL_MAX_SECONDS - + FishingSpotType.BITE_LONG_MAX_SECONDS - ) - * 0.5 - + very_long_probability - * ( - FishingSpotType.BITE_LONG_MAX_SECONDS - + FishingSpotType.BITE_MAX_SECONDS - ) - * 0.5 - ) - assert(absf( - quick_ratio - FishingSpotType.BITE_QUICK_PROBABILITY - ) <= 0.02) + assert(quick_count >= 2300 and quick_count <= 2700) assert(typical_or_long_count > quick_count) - assert(absf(very_long_ratio - very_long_probability) <= 0.005) - assert(absf( - average_wait_seconds - expected_average_wait_seconds - ) <= 1.0) + assert(very_long_count >= 100 and very_long_count <= 300) + assert(average_wait_seconds >= 45.0) + assert(average_wait_seconds <= 49.0) fishing_spot.queue_free() await process_frame @@ -341,16 +305,11 @@ func _validate_remote_presentation() -> void: fish_catch.sale_value = 1 assert(fish_catch.is_valid()) presentation.play_return(fish_catch) + await create_timer(0.6).timeout var catch_display := player.find_child( "CatchDisplay", true, false ) as Node3D assert(catch_display != null) - var showcase_deadline_msec: int = Time.get_ticks_msec() + 2000 - while ( - not catch_display.visible - and Time.get_ticks_msec() < showcase_deadline_msec - ): - await process_frame assert(catch_display.visible) await presentation.return_completed assert(not catch_display.visible) diff --git a/tests/inventory_notepad_art_validation.gd b/tests/inventory_notepad_art_validation.gd index cba0797..cbe3414 100644 --- a/tests/inventory_notepad_art_validation.gd +++ b/tests/inventory_notepad_art_validation.gd @@ -62,9 +62,7 @@ func _run() -> void: await _capture_inventory_pages(player_menu) print("Inventory notepad artwork validation: PASS") presentation_stage.queue_free() - for _frame: int in 10: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() diff --git a/tests/job_multiplayer_validation.gd b/tests/job_multiplayer_validation.gd index b40fc2e..5994eae 100644 --- a/tests/job_multiplayer_validation.gd +++ b/tests/job_multiplayer_validation.gd @@ -56,9 +56,7 @@ func _run_host() -> void: print("Job multiplayer host validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() @@ -95,9 +93,7 @@ func _run_client() -> void: print("Job multiplayer client validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() diff --git a/tests/job_system_validation.gd b/tests/job_system_validation.gd index b6036eb..6061a31 100644 --- a/tests/job_system_validation.gd +++ b/tests/job_system_validation.gd @@ -187,10 +187,8 @@ func _run() -> void: assert(int(save_data.get("save_version", -1)) == 7) assert(PlayerJobService.validate_save_data(save_data.get("jobs", {}))) - session.disconnect_session("Job system validation complete.") main.queue_free() - for _frame: int in 4: - await process_frame + await process_frame print("Job system validation: PASS") quit() diff --git a/tests/logbook_runtime_validation.gd b/tests/logbook_runtime_validation.gd index 3df30a6..77280ea 100644 --- a/tests/logbook_runtime_validation.gd +++ b/tests/logbook_runtime_validation.gd @@ -79,15 +79,10 @@ func _run() -> void: "_show_section_immediate", PlayerMenu.Section.PROFILE ) await process_frame - assert(bool( + assert(not bool( player_menu.call("_handle_direct_page_shortcut", shortcut) )) - await create_timer(0.5).timeout assert(player_menu.visible) - assert(logbook.visible) - assert( - player_menu.get("_current_section") == PlayerMenu.Section.LOGBOOK - ) player_menu.close_menu(PlayerMenu.CloseReason.TEARDOWN, false) print( @@ -95,8 +90,7 @@ func _run() -> void: % [root.size.x, root.size.y] ) main.queue_free() - for _frame: int in 4: - await process_frame + await process_frame quit() diff --git a/tests/logbook_validation.gd b/tests/logbook_validation.gd index 866a34f..38b7ade 100644 --- a/tests/logbook_validation.gd +++ b/tests/logbook_validation.gd @@ -171,7 +171,7 @@ func _validate_page() -> void: page.activate() await process_frame assert(page.get("_category") == WaterType.Type.FRESH_WATER) - assert((page.get("_catalog_grid") as GridContainer).columns == 4) + assert((page.get("_catalog_grid") as GridContainer).columns == 5) var initial_detail_body := page.get("_detail_body") as VBoxContainer assert(not initial_detail_body.get_parent() is ScrollContainer) assert( @@ -209,7 +209,7 @@ func _validate_page() -> void: assert(portrait.source_texture == fish.display_texture) assert( portrait.custom_minimum_size - == LogbookPage.CATALOG_PORTRAIT_SIZE + == LogbookPortraitType.ENTRY_FRAME_SIZE ) assert( portrait.expand_mode diff --git a/tests/player_experience_ui_validation.gd b/tests/player_experience_ui_validation.gd index 31ed497..93c23eb 100644 --- a/tests/player_experience_ui_validation.gd +++ b/tests/player_experience_ui_validation.gd @@ -12,14 +12,6 @@ func _run() -> void: root.add_child(game_ui) await process_frame game_ui.set("_gameplay_ui_enabled", true) - var panel := game_ui.get_node("%ExperienceProgressPanel") as PanelContainer - var bubble := game_ui.get_node("%ExperienceBubble") as PanelContainer - var bubble_label := game_ui.get_node("%ExperienceBubbleLabel") as Label - var award_label := game_ui.get_node("%ExperienceAwardLabel") as Label - assert(panel != null and bubble != null) - assert(not panel.visible and not bubble.visible) - game_ui.call("_on_showcase_changed", "bluegill", "common", 1.0, 0, true) - await process_frame game_ui.call( "_on_experience_awarded", 50, @@ -28,6 +20,13 @@ func _run() -> void: 1, 1, ) + var panel := game_ui.get_node("%ExperienceProgressPanel") as PanelContainer + var bubble := game_ui.get_node("%ExperienceBubble") as PanelContainer + var bubble_label := game_ui.get_node("%ExperienceBubbleLabel") as Label + var award_label := game_ui.get_node("%ExperienceAwardLabel") as Label + assert(panel != null and bubble != null) + assert(not panel.visible and not bubble.visible) + game_ui.call("_on_showcase_changed", "bluegill", "common", 1.0, 0, true) await process_frame assert(not panel.visible and not bubble.visible) game_ui.call("_on_showcase_changed", "", "", 0.0, 0, false) @@ -44,8 +43,5 @@ func _run() -> void: await create_timer(1.2).timeout assert(not panel.visible and not bubble.visible) game_ui.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout print("Player experience UI validation: PASS") quit() diff --git a/tests/surface_drawing_multiplayer_validation.gd b/tests/surface_drawing_multiplayer_validation.gd index 7b4d8d6..12925ba 100644 --- a/tests/surface_drawing_multiplayer_validation.gd +++ b/tests/surface_drawing_multiplayer_validation.gd @@ -114,9 +114,7 @@ func _run_host() -> void: print("Surface drawing multiplayer host validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() @@ -209,9 +207,7 @@ func _run_client() -> void: print("Surface drawing multiplayer client validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() diff --git a/tests/surface_drawing_runtime_validation.gd b/tests/surface_drawing_runtime_validation.gd index b8c00d9..6e6e68d 100644 --- a/tests/surface_drawing_runtime_validation.gd +++ b/tests/surface_drawing_runtime_validation.gd @@ -110,9 +110,7 @@ func _run() -> void: print("Surface drawing runtime validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() diff --git a/tests/surface_drawing_validation.gd b/tests/surface_drawing_validation.gd index 596cec0..aa94f0f 100644 --- a/tests/surface_drawing_validation.gd +++ b/tests/surface_drawing_validation.gd @@ -21,8 +21,6 @@ func _run() -> void: await _validate_blocked_author_visibility() _validate_peer_capability_tracking() print("Surface drawing validation: PASS") - for _frame: int in 4: - await process_frame quit() @@ -120,7 +118,6 @@ func _validate_art_unlocks() -> void: assert(unlocks.is_brush_size_unlocked(4)) assert(unlocks.is_grid_size_unlocked(128)) assert(not unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK + 1)) - unlocks.free() func _validate_grid_snapping() -> void: @@ -268,7 +265,6 @@ func _validate_blocked_author_visibility() -> void: }) var world := Node3D.new() root.add_child(world) - world.add_child(relationships) var canvas := SurfaceDrawingCanvas.new() world.add_child(canvas) var state: Dictionary = { diff --git a/tests/ui_scaling_runtime_validation.gd b/tests/ui_scaling_runtime_validation.gd index f7144eb..d329755 100644 --- a/tests/ui_scaling_runtime_validation.gd +++ b/tests/ui_scaling_runtime_validation.gd @@ -205,8 +205,6 @@ func _run() -> void: presenter.queue_free() root.size = original_size - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame print("UI scaling runtime validation: PASS") quit() diff --git a/tests/world_time_multiplayer_validation.gd b/tests/world_time_multiplayer_validation.gd index 6801de0..2d82aea 100644 --- a/tests/world_time_multiplayer_validation.gd +++ b/tests/world_time_multiplayer_validation.gd @@ -83,9 +83,7 @@ func _run_host() -> void: print("World time multiplayer host validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() @@ -197,9 +195,7 @@ func _run_client() -> void: print("World time multiplayer client validation: PASS") session.disconnect_session("") main.queue_free() - for _frame: int in 4: - await process_frame - await create_timer(0.1).timeout + await process_frame quit() diff --git a/ui/controller_focus_presentation.gd b/ui/controller_focus_presentation.gd index 7223ca7..88954ac 100644 --- a/ui/controller_focus_presentation.gd +++ b/ui/controller_focus_presentation.gd @@ -41,8 +41,6 @@ func _input(event: InputEvent) -> void: CONTROLLER_MOTION_THRESHOLD ): _set_controller_active(true) - elif event is InputEventMouseMotion: - _set_controller_active(false) elif event is InputEventMouseButton: if (event as InputEventMouseButton).pressed: _set_controller_active(false) diff --git a/ui/controller_focus_recovery.gd b/ui/controller_focus_recovery.gd index bcaf6c8..55bc6ad 100644 --- a/ui/controller_focus_recovery.gd +++ b/ui/controller_focus_recovery.gd @@ -3,28 +3,11 @@ extends Node const CONTROLLER_AXIS_THRESHOLD: float = 0.35 const SEMANTIC_MATCH_BONUS: float = 1000000.0 -const KEYBOARD_NAVIGATION_ACTIONS: Array[StringName] = [ - &"ui_accept", - &"ui_cancel", - &"ui_up", - &"ui_down", - &"ui_left", - &"ui_right", - &"ui_focus_next", - &"ui_focus_prev", - &"ui_page_up", - &"ui_page_down", - &"ui_home", - &"ui_end", -] var _controller_active: bool = false -var _focus_navigation_active: bool = false var _last_focus_center: Vector2 = Vector2.ZERO var _last_focus_key: String = "" var _scope_chain: Array[WeakRef] = [] -var _pending_focus: WeakRef -var _pointer_button_down: bool = false var _recovery_generation: int = 0 @@ -44,51 +27,23 @@ func _input(event: InputEvent) -> void: if event is InputEventJoypadButton: var button_event := event as InputEventJoypadButton if button_event.pressed: - _pointer_button_down = false _controller_active = true - _focus_navigation_active = true if button_event.button_index == JOY_BUTTON_LEFT_SHOULDER: _recovery_generation += 1 _scope_chain.clear() - _pending_focus = null - return - _request_pending_focus() return if event is InputEventJoypadMotion: if absf((event as InputEventJoypadMotion).axis_value) >= ( CONTROLLER_AXIS_THRESHOLD ): - _pointer_button_down = false _controller_active = true - _focus_navigation_active = true - _request_pending_focus() - return - if event is InputEventMouseMotion: - _pointer_button_down = ( - (event as InputEventMouseMotion).button_mask != 0 - ) - _leave_focus_navigation(not _pointer_button_down) return if event is InputEventMouseButton: - var mouse_button := event as InputEventMouseButton - if mouse_button.button_index in [ - MOUSE_BUTTON_LEFT, - MOUSE_BUTTON_RIGHT, - MOUSE_BUTTON_MIDDLE, - ]: - _pointer_button_down = mouse_button.pressed - _leave_focus_navigation(false) - if not mouse_button.pressed: - _release_current_pointer_focus.call_deferred() + if (event as InputEventMouseButton).pressed: + _controller_active = false return if event is InputEventKey and (event as InputEventKey).pressed: - _pointer_button_down = false _controller_active = false - if _is_keyboard_navigation_event(event): - _focus_navigation_active = true - _request_pending_focus() - else: - _leave_focus_navigation() func _process(_delta: float) -> void: @@ -103,16 +58,8 @@ func _process(_delta: float) -> void: func _on_gui_focus_changed(control: Control) -> void: _recovery_generation += 1 if control != null: - if _focus_navigation_active: - _pending_focus = null - if _controller_active and control is BaseButton: - _remember_focus(control) - return - if _keeps_pointer_focus(control): - return - if _is_focusable(control): - _pending_focus = weakref(control) - _release_focus_if_inactive.call_deferred(control) + if _controller_active and control is BaseButton: + _remember_focus(control) return if not _controller_active or _scope_chain.is_empty(): return @@ -145,18 +92,13 @@ func _recover_focus(generation: int) -> void: scope == null or not is_instance_valid(scope) or not scope.is_inside_tree() + or not scope.is_visible_in_tree() ): continue - if not scope.is_visible_in_tree(): - _scope_chain.clear() - return var replacement := _best_replacement_in(scope) if replacement != null: replacement.grab_focus() - else: - _scope_chain.clear() - return - _scope_chain.clear() + return func _best_replacement_in(scope: Control) -> Control: @@ -182,7 +124,7 @@ func _is_focusable(control: Control) -> bool: control == null or not control.is_inside_tree() or not control.is_visible_in_tree() - or control.focus_mode not in [Control.FOCUS_CLICK, Control.FOCUS_ALL] + or control.focus_mode == Control.FOCUS_NONE ): return false var button := control as BaseButton @@ -198,80 +140,3 @@ func _semantic_key(control: Control) -> String: tooltip = tooltip.trim_suffix(" (show variants)") tooltip = tooltip.trim_suffix(" (hide variants)") return "%s|%s|%s" % [control.get_class(), label, tooltip] - - -func _leave_focus_navigation(release_focus: bool = true) -> void: - _controller_active = false - _focus_navigation_active = false - _recovery_generation += 1 - _scope_chain.clear() - var focus_owner: Control = get_viewport().gui_get_focus_owner() - if focus_owner == null or _keeps_pointer_focus(focus_owner): - return - if _is_focusable(focus_owner): - _pending_focus = weakref(focus_owner) - if release_focus: - _release_focus_if_inactive.call_deferred(focus_owner) - - -func _release_focus_if_inactive(control: Control) -> void: - if ( - _focus_navigation_active - or _pointer_button_down - or control == null - or not is_instance_valid(control) - or get_viewport().gui_get_focus_owner() != control - or _keeps_pointer_focus(control) - ): - return - get_viewport().gui_release_focus() - - -func _release_current_pointer_focus() -> void: - var focus_owner: Control = get_viewport().gui_get_focus_owner() - if focus_owner != null: - _release_focus_if_inactive(focus_owner) - - -func _request_pending_focus() -> void: - if ( - not _focus_navigation_active - or _pending_focus == null - or get_viewport().gui_get_focus_owner() != null - ): - return - _restore_pending_focus.call_deferred(_recovery_generation) - - -func _restore_pending_focus(generation: int) -> void: - if ( - generation != _recovery_generation - or not _focus_navigation_active - or _pending_focus == null - or get_viewport().gui_get_focus_owner() != null - ): - return - var target := _pending_focus.get_ref() as Control - if not _is_focusable(target): - _pending_focus = null - return - _pending_focus = null - target.grab_focus() - - -func _is_keyboard_navigation_event(event: InputEvent) -> bool: - if event.is_action_pressed(&"open_backpack"): - return false - if ( - event.is_action_pressed(&"ui_cancel") - and get_viewport().gui_get_focus_owner() == null - ): - return false - for action: StringName in KEYBOARD_NAVIGATION_ACTIONS: - if event.is_action_pressed(action): - return true - return false - - -func _keeps_pointer_focus(control: Control) -> bool: - return control is LineEdit or control is TextEdit diff --git a/ui/fishing_shop.gd b/ui/fishing_shop.gd index 3c7d7f4..9f93cf8 100644 --- a/ui/fishing_shop.gd +++ b/ui/fishing_shop.gd @@ -74,8 +74,6 @@ const SUPPLY_PRICE_HORIZONTAL_PADDING: float = 12.0 @onready var _wallet_label: Label = %WalletLabel @onready var _shop_panel: PanelContainer = %ShopPanel -@onready var _shop_panel_margin: MarginContainer = $ShopPanel/Margin -@onready var _shop_panel_layout: VBoxContainer = $ShopPanel/Margin/Layout @onready var _shop_cooler_page: Control = %ShopCoolerPage @onready var _shop_cooler_mount: Control = %ShopCoolerMount @onready var _shop_body: HBoxContainer = $ShopPanel/Margin/Layout/Body @@ -369,7 +367,6 @@ func get_shop_cooler_mount() -> Control: func activate_shop_cooler_page() -> void: _cooler_page_active = true _shop_panel.show() - _set_shop_panel_background_pointer_blocking(false) _shop_body.hide() _feedback.hide() _shop_cooler_page.show() @@ -380,7 +377,6 @@ func deactivate_shop_cooler_page() -> void: _shop_tab_bar.show() _cooler_page_active = false _cooler_modal_open = false - _set_shop_panel_background_pointer_blocking(true) for tab: OrganizerTab in _shop_tabs: tab.disabled = false _shop_cooler_page.hide() @@ -389,17 +385,6 @@ func deactivate_shop_cooler_page() -> void: _feedback.show() -func _set_shop_panel_background_pointer_blocking(blocking: bool) -> void: - var mouse_filter := ( - Control.MOUSE_FILTER_STOP - if blocking - else Control.MOUSE_FILTER_IGNORE - ) - _shop_panel.mouse_filter = mouse_filter - _shop_panel_margin.mouse_filter = mouse_filter - _shop_panel_layout.mouse_filter = mouse_filter - - func set_shop_cooler_modal_open(is_open: bool) -> void: _cooler_modal_open = is_open for tab: OrganizerTab in _shop_tabs: diff --git a/ui/pause_menu.gd b/ui/pause_menu.gd index dccac73..3d2baa8 100644 --- a/ui/pause_menu.gd +++ b/ui/pause_menu.gd @@ -351,10 +351,7 @@ func _open_confirmation( else BubbleConfirmationPageType.InitialFocus.CONFIRM ) ) - # Confirmation actions reject input while the root page is still leaving. - # Finish that exit before exposing an interactive confirmation page so a - # pointer click can never land in the overlap window and be discarded. - _begin_root_exit(_show_confirmation, false) + _begin_root_exit(_show_confirmation) func _show_confirmation() -> void: @@ -453,10 +450,7 @@ func _finish_root_entry(generation: int) -> void: _root_transition_active = false -func _begin_root_exit( - completed: Callable, - overlap_next_page: bool = true, -) -> void: +func _begin_root_exit(completed: Callable) -> void: if _root_transition_active or not _root_page.visible: return _root_transition_generation += 1 @@ -464,14 +458,9 @@ func _begin_root_exit( _emit_transition_flurry() var generation: int = _root_transition_generation _root_page.transition_out( - _finish_root_exit.bind( - generation, - Callable() if overlap_next_page else completed, - ), + _finish_root_exit.bind(generation), 0.0 ) - if not overlap_next_page: - return await get_tree().create_timer( UIMotion.BUBBLE_TRANSITION_OVERLAP_DELAY ).timeout @@ -482,13 +471,10 @@ func _begin_root_exit( func _finish_root_exit( generation: int, - completed: Callable, ) -> void: if generation != _root_transition_generation or not visible: return _root_transition_active = false - if completed.is_valid(): - completed.call() func _finish_user_close() -> void: diff --git a/ui/title_screen.tscn b/ui/title_screen.tscn index 285da68..040aebe 100644 --- a/ui/title_screen.tscn +++ b/ui/title_screen.tscn @@ -209,7 +209,7 @@ unique_name_in_owner = true layout_mode = 2 theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1) theme_override_font_sizes/font_size = 22 -text = "v0.6.7-alpha" +text = "v0.6.5-alpha" horizontal_alignment = 1 [node name="Spacer" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent"]