diff --git a/network/network_surface_drawing_service.gd b/network/network_surface_drawing_service.gd index c322e5b..485dfaa 100644 --- a/network/network_surface_drawing_service.gd +++ b/network/network_surface_drawing_service.gd @@ -250,7 +250,11 @@ func activate( _pointer_screen_position = _clamped_pointer_position( initial_pointer_position ) - _prior_mouse_mode = Input.mouse_mode + # Art-kit activation can occur while the prior right-click camera drag is + # still held. Stop that drag before recording a return mode, then restore + # normal camera availability so right-click continues to work while drawing. + _stop_camera_drag_for_art_kit() + _prior_mouse_mode = _get_restorable_mouse_mode(Input.mouse_mode) Input.mouse_mode = Input.MOUSE_MODE_VISIBLE _update_aim() _refresh_stencil_visibility() @@ -266,6 +270,10 @@ func deactivate() -> void: _eraser_mode = false _clear_armed_guide_action(false) _camera_look_active = false + # Do not leave a drag captured when drawing is closed by a menu, hotbar + # change, or session transition. The next camera drag must start on a fresh + # physical right-click press. + _stop_camera_drag_for_art_kit() _reset_stroke() _selected_canvas_id = "" _hovered_canvas_id = "" @@ -273,10 +281,24 @@ func deactivate() -> void: _aim_hit.clear() _hide_previews() _refresh_stencil_visibility() - Input.mouse_mode = _prior_mouse_mode + Input.mouse_mode = _get_restorable_mouse_mode(_prior_mouse_mode) _emit_hud_state("") +func _stop_camera_drag_for_art_kit() -> void: + if _local_player == null or not is_instance_valid(_local_player): + return + var camera_input_enabled: bool = _local_player.is_camera_input_enabled() + _local_player.set_camera_input_enabled(false) + _local_player.set_camera_input_enabled(camera_input_enabled) + + +func _get_restorable_mouse_mode(mouse_mode: Input.MouseMode) -> Input.MouseMode: + if mouse_mode == Input.MOUSE_MODE_CAPTURED: + return Input.MOUSE_MODE_VISIBLE + return mouse_mode + + func is_active() -> bool: return _active diff --git a/ui/decor_shop.gd b/ui/decor_shop.gd index 9292806..d05d9eb 100644 --- a/ui/decor_shop.gd +++ b/ui/decor_shop.gd @@ -82,9 +82,11 @@ func open_shop() -> bool: return false _prior_movement_enabled = _player.is_movement_enabled() _prior_camera_enabled = _player.is_camera_input_enabled() - _prior_mouse_mode = Input.mouse_mode _player.set_movement_enabled(false) + # Release any active right-click drag first. The shop must not remember the + # drag's transient captured mode because release may happen inside the UI. _player.set_camera_input_enabled(false) + _prior_mouse_mode = _get_restorable_mouse_mode(Input.mouse_mode) _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true) Input.mouse_mode = Input.MOUSE_MODE_VISIBLE _transaction_pending = ( @@ -133,6 +135,12 @@ func close_for_session_end() -> void: close_shop(false) +func _get_restorable_mouse_mode(mouse_mode: Input.MouseMode) -> Input.MouseMode: + if mouse_mode == Input.MOUSE_MODE_CAPTURED: + return Input.MOUSE_MODE_VISIBLE + return mouse_mode + + func _unhandled_input(event: InputEvent) -> void: if not visible or not event.is_action_pressed("ui_cancel"): return diff --git a/ui/fishing_shop.gd b/ui/fishing_shop.gd index ad8247c..234a1c1 100644 --- a/ui/fishing_shop.gd +++ b/ui/fishing_shop.gd @@ -654,11 +654,14 @@ func open_shop() -> bool: ) _prior_movement_enabled = _player.is_movement_enabled() _prior_camera_enabled = _player.is_camera_input_enabled() - _prior_mouse_mode = Input.mouse_mode + _player.set_movement_enabled(false) + # Releasing an active camera drag before the snapshot prevents the close + # path from restoring a captured pointer after its matching button release + # has been consumed by the shop. + _player.set_camera_input_enabled(false) + _prior_mouse_mode = _get_restorable_mouse_mode(Input.mouse_mode) _snapshot_stored = true _mouse_snapshot_stored = true - _player.set_movement_enabled(false) - _player.set_camera_input_enabled(false) _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true) Input.mouse_mode = Input.MOUSE_MODE_VISIBLE _set_feedback("") @@ -1897,3 +1900,9 @@ func _apply_mouse_close_policy(reason: CloseReason) -> void: CloseReason.TEARDOWN: pass _mouse_snapshot_stored = false + + +func _get_restorable_mouse_mode(mouse_mode: Input.MouseMode) -> Input.MouseMode: + if mouse_mode == Input.MOUSE_MODE_CAPTURED: + return Input.MOUSE_MODE_VISIBLE + return mouse_mode diff --git a/ui/player_menu.gd b/ui/player_menu.gd index 2f9f96f..9b15913 100644 --- a/ui/player_menu.gd +++ b/ui/player_menu.gd @@ -1559,11 +1559,14 @@ func open_menu() -> void: _cancel_page_tween() _prior_movement_enabled = _player.is_movement_enabled() _prior_camera_input_enabled = _player.is_camera_input_enabled() - _prior_mouse_mode = Input.mouse_mode + _player.set_movement_enabled(false) + # Ending an active right-click camera drag restores its ordinary pointer + # mode. Snapshot after that handoff so this menu never restores the drag's + # transient captured mode when it closes. + _player.set_camera_input_enabled(false) + _prior_mouse_mode = _get_restorable_mouse_mode(Input.mouse_mode) _control_snapshot_stored = true _mouse_snapshot_stored = true - _player.set_movement_enabled(false) - _player.set_camera_input_enabled(false) _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true) Input.mouse_mode = Input.MOUSE_MODE_VISIBLE visible = true @@ -1775,6 +1778,16 @@ func _apply_mouse_close_policy(reason: CloseReason) -> void: _mouse_snapshot_stored = false +func _get_restorable_mouse_mode(mouse_mode: Input.MouseMode) -> Input.MouseMode: + # Mouse capture belongs to an in-progress camera drag, never to the player + # menu itself. A held right button can be released while this menu owns + # input, so restoring CAPTURED here would leave the pointer hidden without + # an active drag to release it. + if mouse_mode == Input.MOUSE_MODE_CAPTURED: + return Input.MOUSE_MODE_VISIBLE + return mouse_mode + + func _on_bite_activated() -> void: if visible: close_menu(CloseReason.BITE_STARTED) diff --git a/ui/player_storage.gd b/ui/player_storage.gd index 1f553ea..83817c5 100644 --- a/ui/player_storage.gd +++ b/ui/player_storage.gd @@ -109,9 +109,12 @@ func _open_storage( _decorate_button.visible = home_access and allow_decorating _prior_movement_enabled = _player.is_movement_enabled() _prior_camera_enabled = _player.is_camera_input_enabled() - _prior_mouse_mode = Input.mouse_mode _player.set_movement_enabled(false) + # End an in-progress right-click camera drag before taking the snapshot. + # A held button can be released while this page owns input, so captured mode + # is never a valid mouse mode to restore when the page closes. _player.set_camera_input_enabled(false) + _prior_mouse_mode = _get_restorable_mouse_mode(Input.mouse_mode) _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true) Input.mouse_mode = Input.MOUSE_MODE_VISIBLE _feedback.text = "select an item to move it ยท drag to choose a slot" @@ -153,6 +156,12 @@ func close_for_session_end() -> void: close_storage(false) +func _get_restorable_mouse_mode(mouse_mode: Input.MouseMode) -> Input.MouseMode: + if mouse_mode == Input.MOUSE_MODE_CAPTURED: + return Input.MOUSE_MODE_VISIBLE + return mouse_mode + + func consume_escape() -> bool: if not visible: return false