Improve Android controller and chat support
This commit is contained in:
parent
3dc8ced5d2
commit
ceef03eba5
24 changed files with 2485 additions and 128 deletions
244
ui/chat_ui.gd
244
ui/chat_ui.gd
|
|
@ -20,6 +20,11 @@ const HANDLE_SIZE := Vector2(34, 42)
|
|||
const HANDLE_GAP: float = 6.0
|
||||
const COLLAPSED_REVEAL_WIDTH: float = 8.0
|
||||
const HINT_EDGE_MARGIN: float = 4.0
|
||||
const MOBILE_COMPACT_WIDTH: float = 620.0
|
||||
const MOBILE_EXPANDED_WIDTH: float = 820.0
|
||||
const MOBILE_COMPACT_HEIGHT: float = 220.0
|
||||
const MOBILE_EXPANDED_HEIGHT: float = 390.0
|
||||
const MOBILE_EDGE_MARGIN: float = 12.0
|
||||
const CLOCK_SIZE := Vector2(116.0, 34.0)
|
||||
const CLOCK_EDGE_MARGIN: float = 10.0
|
||||
const WEATHER_ICON_SIZE := Vector2(34.0, 34.0)
|
||||
|
|
@ -72,8 +77,10 @@ var _send_pending: bool = false
|
|||
var _pending_send_body: String = ""
|
||||
var _prior_movement: bool = true
|
||||
var _prior_camera: bool = true
|
||||
var _controller_refocused: bool = false
|
||||
var _output_scale: float = 1.0
|
||||
var _dock_right: bool = false
|
||||
var _mobile_mode: bool = false
|
||||
var _world_time: WorldTimeServiceType
|
||||
var _world_weather: WorldWeatherServiceType
|
||||
|
||||
|
|
@ -125,6 +132,7 @@ func setup(
|
|||
_world_weather.get_weather(),
|
||||
_world_weather.get_seconds_remaining(),
|
||||
)
|
||||
set_mobile_mode(_settings.current_settings.chat_mobile_mode)
|
||||
set_dock_right(_settings.current_settings.chat_dock_right)
|
||||
_service.message_received.connect(_on_message)
|
||||
_service.local_message_confirmed.connect(_on_local_message_confirmed)
|
||||
|
|
@ -151,6 +159,7 @@ func open_chat() -> void:
|
|||
return
|
||||
if _presentation_state == PresentationState.COLLAPSED:
|
||||
_set_presentation_state(_visible_state_before_collapse, true, true)
|
||||
_controller_refocused = false
|
||||
_opened = true
|
||||
text_entry_ownership_changed.emit(true)
|
||||
_prior_movement = _player.is_movement_enabled()
|
||||
|
|
@ -162,8 +171,10 @@ func open_chat() -> void:
|
|||
# prediction would let a remote host continue the last held movement.
|
||||
_session.submit_neutral_local_movement()
|
||||
_entry.show()
|
||||
_entry.virtual_keyboard_enabled = false
|
||||
_entry.grab_focus()
|
||||
_hint.hide()
|
||||
_refresh_input_ownership()
|
||||
_update_panel_opacity(true)
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
|
||||
|
|
@ -184,6 +195,7 @@ func close_chat() -> void:
|
|||
return
|
||||
_opened = false
|
||||
text_entry_ownership_changed.emit(false)
|
||||
_entry.virtual_keyboard_enabled = false
|
||||
_entry.release_focus()
|
||||
_entry.hide()
|
||||
_player.set_movement_enabled(_prior_movement)
|
||||
|
|
@ -191,14 +203,62 @@ func close_chat() -> void:
|
|||
_fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false)
|
||||
_flush_draft()
|
||||
_refresh_visibility()
|
||||
_refresh_input_ownership()
|
||||
|
||||
|
||||
func toggle_chat() -> void:
|
||||
if _presentation_state == PresentationState.COLLAPSED:
|
||||
open_chat()
|
||||
return
|
||||
if _opened:
|
||||
close_chat()
|
||||
_set_presentation_state(PresentationState.COLLAPSED, true, true)
|
||||
return
|
||||
if _controller_refocused:
|
||||
_controller_refocused = false
|
||||
_set_presentation_state(PresentationState.COLLAPSED, true, true)
|
||||
return
|
||||
open_chat()
|
||||
|
||||
|
||||
func refocus_gameplay() -> void:
|
||||
close_chat()
|
||||
_controller_refocused = true
|
||||
get_viewport().gui_release_focus()
|
||||
|
||||
|
||||
func toggle_focus() -> void:
|
||||
if _presentation_state == PresentationState.COLLAPSED:
|
||||
return
|
||||
if _opened:
|
||||
refocus_gameplay()
|
||||
else:
|
||||
open_chat()
|
||||
|
||||
|
||||
func request_virtual_keyboard() -> bool:
|
||||
if not _opened or not _entry.has_focus():
|
||||
return false
|
||||
_entry.virtual_keyboard_enabled = true
|
||||
# Re-entering focus after the controller accept event is the explicit
|
||||
# request Android uses to display its keyboard. Merely focusing Chat keeps
|
||||
# the keyboard disabled so the world remains readable.
|
||||
_entry.release_focus()
|
||||
_entry.call_deferred("grab_focus")
|
||||
return true
|
||||
|
||||
|
||||
func is_open() -> bool:
|
||||
return _opened
|
||||
|
||||
|
||||
func is_collapsed() -> bool:
|
||||
return _presentation_state == PresentationState.COLLAPSED
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("open_chat"):
|
||||
if _opened:
|
||||
close_chat()
|
||||
elif _available:
|
||||
open_chat()
|
||||
toggle_chat()
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if event is InputEventKey and event.pressed and not event.echo:
|
||||
|
|
@ -404,14 +464,21 @@ func _build_ui() -> void:
|
|||
_unread_indicator.hide()
|
||||
_hint.hide()
|
||||
_layout_presentation(false)
|
||||
_refresh_input_ownership()
|
||||
|
||||
|
||||
func _chat_panel_style() -> StyleBoxFlat:
|
||||
var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94))
|
||||
style.corner_radius_top_left = 10 if _dock_right else 0
|
||||
style.corner_radius_bottom_left = 10 if _dock_right else 0
|
||||
style.corner_radius_top_right = 0 if _dock_right else 10
|
||||
style.corner_radius_bottom_right = 0 if _dock_right else 10
|
||||
if _mobile_mode:
|
||||
style.corner_radius_top_left = 0
|
||||
style.corner_radius_top_right = 0
|
||||
style.corner_radius_bottom_left = 10
|
||||
style.corner_radius_bottom_right = 10
|
||||
else:
|
||||
style.corner_radius_top_left = 10 if _dock_right else 0
|
||||
style.corner_radius_bottom_left = 10 if _dock_right else 0
|
||||
style.corner_radius_top_right = 0 if _dock_right else 10
|
||||
style.corner_radius_bottom_right = 0 if _dock_right else 10
|
||||
style.content_margin_left = 12
|
||||
style.content_margin_right = 12
|
||||
style.content_margin_top = 10
|
||||
|
|
@ -419,6 +486,18 @@ func _chat_panel_style() -> StyleBoxFlat:
|
|||
return style
|
||||
|
||||
|
||||
func _refresh_input_ownership() -> void:
|
||||
if _panel == null or _history == null:
|
||||
return
|
||||
var filter: Control.MouseFilter = (
|
||||
Control.MOUSE_FILTER_STOP
|
||||
if _opened
|
||||
else Control.MOUSE_FILTER_IGNORE
|
||||
)
|
||||
_panel.mouse_filter = filter
|
||||
_history.mouse_filter = filter
|
||||
|
||||
|
||||
func _clock_panel_style() -> StyleBoxFlat:
|
||||
var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94))
|
||||
style.set_corner_radius_all(12)
|
||||
|
|
@ -620,7 +699,10 @@ func _refresh_visibility() -> void:
|
|||
_collapse_button.show()
|
||||
var collapsed := _presentation_state == PresentationState.COLLAPSED
|
||||
_panel.show()
|
||||
_height_button.show()
|
||||
_height_button.visible = not _mobile_mode
|
||||
_height_button.focus_mode = (
|
||||
Control.FOCUS_NONE if _mobile_mode else Control.FOCUS_ALL
|
||||
)
|
||||
_unread_indicator.visible = collapsed and _collapsed_has_unread
|
||||
_hint.visible = not _opened
|
||||
|
||||
|
|
@ -670,10 +752,31 @@ func is_docked_right() -> bool:
|
|||
return _dock_right
|
||||
|
||||
|
||||
func set_mobile_mode(enabled: bool) -> void:
|
||||
_mobile_mode = enabled
|
||||
if not is_node_ready() or _panel == null:
|
||||
return
|
||||
if _mobile_mode:
|
||||
if _presentation_state == PresentationState.EXPANDED:
|
||||
_presentation_state = PresentationState.COMPACT
|
||||
if _visible_state_before_collapse == PresentationState.EXPANDED:
|
||||
_visible_state_before_collapse = PresentationState.COMPACT
|
||||
_panel.add_theme_stylebox_override("panel", _chat_panel_style())
|
||||
_refresh_handle_labels(_presentation_state)
|
||||
_refresh_visibility()
|
||||
_layout_presentation(false)
|
||||
|
||||
|
||||
func is_mobile_mode() -> bool:
|
||||
return _mobile_mode
|
||||
|
||||
|
||||
func _refresh_handle_labels(state: PresentationState) -> void:
|
||||
var collapsed := state == PresentationState.COLLAPSED
|
||||
var height_state := _visible_state_before_collapse if collapsed else state
|
||||
if _dock_right:
|
||||
if _mobile_mode:
|
||||
_collapse_button.text = "v" if collapsed else "^"
|
||||
elif _dock_right:
|
||||
_collapse_button.text = "<" if collapsed else ">"
|
||||
else:
|
||||
_collapse_button.text = ">" if collapsed else "<"
|
||||
|
|
@ -798,32 +901,101 @@ func _layout_presentation(animate: bool) -> void:
|
|||
var viewport_height := size.y
|
||||
if viewport_height <= 0.0:
|
||||
viewport_height = 720.0
|
||||
var layout_state := (
|
||||
_visible_state_before_collapse
|
||||
if _presentation_state == PresentationState.COLLAPSED
|
||||
else _presentation_state
|
||||
)
|
||||
var collapsed := _presentation_state == PresentationState.COLLAPSED
|
||||
var bottom_margin := minf(
|
||||
BOTTOM_MARGIN,
|
||||
maxf(MIN_TOP_MARGIN, (viewport_height - MIN_HISTORY_HEIGHT) * 0.25),
|
||||
)
|
||||
var bottom := viewport_height - bottom_margin
|
||||
var height := COMPACT_HEIGHT
|
||||
var layout_state := (
|
||||
_visible_state_before_collapse
|
||||
if _presentation_state == PresentationState.COLLAPSED
|
||||
else _presentation_state
|
||||
)
|
||||
if layout_state == PresentationState.EXPANDED:
|
||||
height = maxf(MIN_HISTORY_HEIGHT, viewport_height - 2.0 * bottom_margin)
|
||||
height = minf(height, maxf(MIN_HISTORY_HEIGHT, bottom - MIN_TOP_MARGIN))
|
||||
var collapsed := _presentation_state == PresentationState.COLLAPSED
|
||||
var panel_x: float
|
||||
if _dock_right:
|
||||
panel_x = (
|
||||
viewport_width - COLLAPSED_REVEAL_WIDTH
|
||||
if collapsed
|
||||
else viewport_width - PANEL_WIDTH
|
||||
var panel_width := PANEL_WIDTH
|
||||
var panel_x: float = 0.0
|
||||
var panel_y: float = 0.0
|
||||
var collapse_position := Vector2.ZERO
|
||||
var height_position := Vector2.ZERO
|
||||
var unread_position := Vector2.ZERO
|
||||
if _mobile_mode:
|
||||
panel_width = (
|
||||
MOBILE_EXPANDED_WIDTH
|
||||
if layout_state == PresentationState.EXPANDED
|
||||
else MOBILE_COMPACT_WIDTH
|
||||
)
|
||||
panel_width = minf(
|
||||
panel_width,
|
||||
maxf(1.0, viewport_width - MOBILE_EDGE_MARGIN * 2.0),
|
||||
)
|
||||
height = (
|
||||
MOBILE_EXPANDED_HEIGHT
|
||||
if layout_state == PresentationState.EXPANDED
|
||||
else MOBILE_COMPACT_HEIGHT
|
||||
)
|
||||
height = minf(
|
||||
height,
|
||||
maxf(MIN_HISTORY_HEIGHT, viewport_height - HANDLE_SIZE.y),
|
||||
)
|
||||
panel_x = (viewport_width - panel_width) * 0.5
|
||||
panel_y = -(height - COLLAPSED_REVEAL_WIDTH) if collapsed else 0.0
|
||||
var mobile_handle_y: float = (
|
||||
COLLAPSED_REVEAL_WIDTH if collapsed else height
|
||||
)
|
||||
collapse_position = Vector2(
|
||||
panel_x + panel_width - HANDLE_SIZE.x,
|
||||
mobile_handle_y,
|
||||
)
|
||||
height_position = Vector2(
|
||||
collapse_position.x - HANDLE_SIZE.x - HANDLE_GAP,
|
||||
mobile_handle_y,
|
||||
)
|
||||
unread_position = collapse_position + Vector2(6.0, -18.0)
|
||||
else:
|
||||
panel_x = -(PANEL_WIDTH - COLLAPSED_REVEAL_WIDTH) if collapsed else 0.0
|
||||
var target_position := Vector2(panel_x, bottom - height)
|
||||
var target_size := Vector2(PANEL_WIDTH, height)
|
||||
if layout_state == PresentationState.EXPANDED:
|
||||
height = maxf(
|
||||
MIN_HISTORY_HEIGHT,
|
||||
viewport_height - 2.0 * bottom_margin,
|
||||
)
|
||||
height = minf(
|
||||
height,
|
||||
maxf(MIN_HISTORY_HEIGHT, bottom - MIN_TOP_MARGIN),
|
||||
)
|
||||
if _dock_right:
|
||||
panel_x = (
|
||||
viewport_width - COLLAPSED_REVEAL_WIDTH
|
||||
if collapsed
|
||||
else viewport_width - PANEL_WIDTH
|
||||
)
|
||||
else:
|
||||
panel_x = (
|
||||
-(PANEL_WIDTH - COLLAPSED_REVEAL_WIDTH)
|
||||
if collapsed
|
||||
else 0.0
|
||||
)
|
||||
panel_y = bottom - height
|
||||
var handle_stack_height := HANDLE_SIZE.y * 2.0 + HANDLE_GAP
|
||||
var handle_stack_top := (
|
||||
bottom - COMPACT_HEIGHT * 0.5 - handle_stack_height * 0.5
|
||||
)
|
||||
var handle_x: float = (
|
||||
panel_x - HANDLE_SIZE.x
|
||||
if _dock_right
|
||||
else COLLAPSED_REVEAL_WIDTH if collapsed else PANEL_WIDTH
|
||||
)
|
||||
collapse_position = Vector2(
|
||||
handle_x,
|
||||
handle_stack_top + HANDLE_SIZE.y + HANDLE_GAP,
|
||||
)
|
||||
height_position = Vector2(handle_x, handle_stack_top)
|
||||
var unread_offset_x := -18.0 if _dock_right else 6.0
|
||||
unread_position = collapse_position + Vector2(
|
||||
unread_offset_x,
|
||||
-18.0,
|
||||
)
|
||||
var target_position := Vector2(panel_x, panel_y)
|
||||
var target_size := Vector2(panel_width, height)
|
||||
var clock_x: float = (
|
||||
viewport_width - CLOCK_SIZE.x - CLOCK_EDGE_MARGIN
|
||||
if _dock_right else CLOCK_EDGE_MARGIN
|
||||
|
|
@ -840,22 +1012,6 @@ func _layout_presentation(animate: bool) -> void:
|
|||
var weather_icon_position := Vector2(
|
||||
weather_icon_x, clock_position.y
|
||||
)
|
||||
var handle_stack_height := HANDLE_SIZE.y * 2.0 + HANDLE_GAP
|
||||
var handle_stack_top := (
|
||||
bottom - COMPACT_HEIGHT * 0.5 - handle_stack_height * 0.5
|
||||
)
|
||||
var handle_x: float
|
||||
if _dock_right:
|
||||
handle_x = panel_x - HANDLE_SIZE.x
|
||||
else:
|
||||
handle_x = COLLAPSED_REVEAL_WIDTH if collapsed else PANEL_WIDTH
|
||||
var collapse_position := Vector2(
|
||||
handle_x,
|
||||
handle_stack_top + HANDLE_SIZE.y + HANDLE_GAP,
|
||||
)
|
||||
var height_position := Vector2(handle_x, handle_stack_top)
|
||||
var unread_offset_x := -18.0 if _dock_right else 6.0
|
||||
var unread_position := collapse_position + Vector2(unread_offset_x, -18.0)
|
||||
var hint_position := Vector2(
|
||||
(
|
||||
viewport_width - _hint.size.x - HINT_EDGE_MARGIN
|
||||
|
|
|
|||
450
ui/controller_mapping_panel.gd
Normal file
450
ui/controller_mapping_panel.gd
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
class_name ControllerMappingPanel
|
||||
extends Control
|
||||
|
||||
signal closed
|
||||
|
||||
const ControllerMappingManagerType = preload(
|
||||
"res://settings/controller_mapping_manager.gd"
|
||||
)
|
||||
const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
|
||||
|
||||
var _mapping_manager: ControllerMappingManagerType
|
||||
var _binding_buttons: Dictionary = {}
|
||||
var _controller_label: Label
|
||||
var _instruction_label: Label
|
||||
var _progress_label: Label
|
||||
var _auto_map_button: Button
|
||||
var _reset_button: Button
|
||||
var _close_button: Button
|
||||
var _capturing_role: StringName = &""
|
||||
var _auto_map_active: bool = false
|
||||
var _auto_map_index: int = -1
|
||||
var _auto_map_draft: Dictionary = {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
set_process_input(true)
|
||||
_build_interface()
|
||||
hide()
|
||||
|
||||
|
||||
func setup(mapping_manager: ControllerMappingManagerType) -> void:
|
||||
_mapping_manager = mapping_manager
|
||||
if not _mapping_manager.active_controller_changed.is_connected(
|
||||
_on_active_controller_changed
|
||||
):
|
||||
_mapping_manager.active_controller_changed.connect(
|
||||
_on_active_controller_changed
|
||||
)
|
||||
if not _mapping_manager.active_profile_changed.is_connected(
|
||||
_refresh_bindings
|
||||
):
|
||||
_mapping_manager.active_profile_changed.connect(_refresh_bindings)
|
||||
_refresh_bindings()
|
||||
|
||||
|
||||
func open_panel() -> void:
|
||||
if _mapping_manager == null:
|
||||
return
|
||||
_cancel_capture()
|
||||
show()
|
||||
_refresh_bindings()
|
||||
_auto_map_button.grab_focus()
|
||||
|
||||
|
||||
func is_open() -> bool:
|
||||
return visible
|
||||
|
||||
|
||||
func is_capturing() -> bool:
|
||||
return visible and not _capturing_role.is_empty()
|
||||
|
||||
|
||||
func request_back() -> void:
|
||||
if not visible:
|
||||
return
|
||||
if not _capturing_role.is_empty():
|
||||
_cancel_capture()
|
||||
return
|
||||
close_panel()
|
||||
|
||||
|
||||
func close_panel() -> void:
|
||||
_cancel_capture()
|
||||
hide()
|
||||
closed.emit()
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if not visible or _mapping_manager == null:
|
||||
return
|
||||
if _capturing_role.is_empty():
|
||||
return
|
||||
var key_event := event as InputEventKey
|
||||
if (
|
||||
key_event != null
|
||||
and key_event.pressed
|
||||
and not key_event.echo
|
||||
and key_event.keycode == KEY_ESCAPE
|
||||
):
|
||||
get_viewport().set_input_as_handled()
|
||||
_cancel_capture()
|
||||
_progress_label.text = "controller mapping cancelled"
|
||||
return
|
||||
if not (
|
||||
event is InputEventJoypadButton
|
||||
or event is InputEventJoypadMotion
|
||||
):
|
||||
return
|
||||
var binding: Dictionary = _mapping_manager.binding_from_event(
|
||||
_capturing_role,
|
||||
event,
|
||||
)
|
||||
if binding.is_empty():
|
||||
return
|
||||
if not _mapping_manager.validate_binding(_capturing_role, binding):
|
||||
return
|
||||
get_viewport().set_input_as_handled()
|
||||
_accept_captured_binding(binding)
|
||||
|
||||
|
||||
func _build_interface() -> void:
|
||||
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
z_index = 100
|
||||
UtilityPageStyleType.apply_page(self)
|
||||
|
||||
var frame := PanelContainer.new()
|
||||
frame.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
frame.offset_left = 18.0
|
||||
frame.offset_top = 18.0
|
||||
frame.offset_right = -18.0
|
||||
frame.offset_bottom = -18.0
|
||||
frame.add_theme_stylebox_override(
|
||||
"panel",
|
||||
UtilityPageStyleType.rounded_style(
|
||||
UtilityPageStyleType.OCEAN_PANEL_DEEP,
|
||||
20,
|
||||
),
|
||||
)
|
||||
add_child(frame)
|
||||
|
||||
var outer_margin := MarginContainer.new()
|
||||
outer_margin.add_theme_constant_override("margin_left", 24)
|
||||
outer_margin.add_theme_constant_override("margin_top", 20)
|
||||
outer_margin.add_theme_constant_override("margin_right", 24)
|
||||
outer_margin.add_theme_constant_override("margin_bottom", 20)
|
||||
frame.add_child(outer_margin)
|
||||
|
||||
var page := VBoxContainer.new()
|
||||
page.add_theme_constant_override("separation", 12)
|
||||
outer_margin.add_child(page)
|
||||
|
||||
var heading_row := HBoxContainer.new()
|
||||
heading_row.add_theme_constant_override("separation", 12)
|
||||
page.add_child(heading_row)
|
||||
|
||||
var title := Label.new()
|
||||
title.text = "controller mapping"
|
||||
title.add_theme_font_size_override("font_size", 28)
|
||||
title.add_theme_color_override(
|
||||
"font_color", UtilityPageStyleType.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
heading_row.add_child(title)
|
||||
|
||||
_controller_label = Label.new()
|
||||
_controller_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
_controller_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_controller_label.add_theme_font_size_override("font_size", 15)
|
||||
_controller_label.add_theme_color_override(
|
||||
"font_color", UtilityPageStyleType.OCEAN_TEXT_SECONDARY
|
||||
)
|
||||
heading_row.add_child(_controller_label)
|
||||
|
||||
_instruction_label = Label.new()
|
||||
_instruction_label.text = (
|
||||
"auto-map walks through a standard xbox-style controller. "
|
||||
+ "select any row afterward to override it."
|
||||
)
|
||||
_instruction_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_instruction_label.add_theme_font_size_override("font_size", 15)
|
||||
_instruction_label.add_theme_color_override(
|
||||
"font_color", UtilityPageStyleType.OCEAN_TEXT_SECONDARY
|
||||
)
|
||||
page.add_child(_instruction_label)
|
||||
|
||||
_progress_label = Label.new()
|
||||
_progress_label.custom_minimum_size.y = 34.0
|
||||
_progress_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_progress_label.add_theme_font_size_override("font_size", 18)
|
||||
_progress_label.add_theme_color_override(
|
||||
"font_color", UtilityPageStyleType.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
page.add_child(_progress_label)
|
||||
|
||||
var scroll := ScrollContainer.new()
|
||||
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
page.add_child(scroll)
|
||||
|
||||
var role_grid := GridContainer.new()
|
||||
role_grid.columns = 2
|
||||
role_grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
role_grid.add_theme_constant_override("h_separation", 12)
|
||||
role_grid.add_theme_constant_override("v_separation", 7)
|
||||
scroll.add_child(role_grid)
|
||||
|
||||
for role: StringName in ControllerMappingManagerType.ROLE_ORDER:
|
||||
var role_label := Label.new()
|
||||
role_label.text = ControllerMappingManagerType.ROLE_LABELS.get(
|
||||
role,
|
||||
str(role),
|
||||
)
|
||||
role_label.custom_minimum_size = Vector2(330.0, 42.0)
|
||||
role_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
role_label.add_theme_font_size_override("font_size", 16)
|
||||
role_label.add_theme_color_override(
|
||||
"font_color", UtilityPageStyleType.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
role_grid.add_child(role_label)
|
||||
|
||||
var binding_button := Button.new()
|
||||
binding_button.custom_minimum_size = Vector2(210.0, 42.0)
|
||||
binding_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
binding_button.focus_mode = Control.FOCUS_ALL
|
||||
binding_button.tooltip_text = (
|
||||
"change " + str(ControllerMappingManagerType.ROLE_LABELS.get(
|
||||
role,
|
||||
str(role),
|
||||
))
|
||||
)
|
||||
UtilityPageStyleType.apply_compact_ocean_button(binding_button)
|
||||
binding_button.pressed.connect(_begin_manual_capture.bind(role))
|
||||
role_grid.add_child(binding_button)
|
||||
_binding_buttons[role] = binding_button
|
||||
|
||||
var footer := HBoxContainer.new()
|
||||
footer.alignment = BoxContainer.ALIGNMENT_END
|
||||
footer.add_theme_constant_override("separation", 10)
|
||||
page.add_child(footer)
|
||||
|
||||
_auto_map_button = Button.new()
|
||||
_auto_map_button.text = "auto-map controller"
|
||||
_auto_map_button.tooltip_text = (
|
||||
"walk through every input on a standard xbox-style controller"
|
||||
)
|
||||
UtilityPageStyleType.apply_ocean_button(_auto_map_button)
|
||||
_auto_map_button.pressed.connect(_begin_auto_map)
|
||||
footer.add_child(_auto_map_button)
|
||||
|
||||
_reset_button = Button.new()
|
||||
_reset_button.text = "restore defaults"
|
||||
_reset_button.tooltip_text = "remove this controller's custom mapping"
|
||||
UtilityPageStyleType.apply_ocean_button(_reset_button)
|
||||
_reset_button.pressed.connect(_reset_mapping)
|
||||
footer.add_child(_reset_button)
|
||||
|
||||
_close_button = Button.new()
|
||||
_close_button.text = "done"
|
||||
UtilityPageStyleType.apply_ocean_button(_close_button)
|
||||
_close_button.pressed.connect(close_panel)
|
||||
footer.add_child(_close_button)
|
||||
|
||||
|
||||
func _begin_auto_map() -> void:
|
||||
if _mapping_manager == null:
|
||||
return
|
||||
_auto_map_active = true
|
||||
_auto_map_index = 0
|
||||
_auto_map_draft = {}
|
||||
_set_capture_role(ControllerMappingManagerType.ROLE_ORDER[_auto_map_index])
|
||||
|
||||
|
||||
func _begin_manual_capture(role: StringName) -> void:
|
||||
_auto_map_active = false
|
||||
_auto_map_index = -1
|
||||
_auto_map_draft.clear()
|
||||
_set_capture_role(role)
|
||||
|
||||
|
||||
func _set_capture_role(role: StringName) -> void:
|
||||
_capturing_role = role
|
||||
_set_action_buttons_disabled(true)
|
||||
if _auto_map_active:
|
||||
_progress_label.text = "step %d of %d: %s" % [
|
||||
_auto_map_index + 1,
|
||||
ControllerMappingManagerType.ROLE_ORDER.size(),
|
||||
_mapping_manager.get_role_prompt(role),
|
||||
]
|
||||
return
|
||||
var input_instruction: String = "press any controller button"
|
||||
if _mapping_manager.role_expects_axis(role):
|
||||
input_instruction = "move any controller axis"
|
||||
elif _mapping_manager.role_accepts_axis(role):
|
||||
input_instruction = "press any button or move any controller axis"
|
||||
_progress_label.text = "%s for %s" % [
|
||||
input_instruction,
|
||||
_mapping_manager.get_role_label(role),
|
||||
]
|
||||
|
||||
|
||||
func _accept_captured_binding(binding: Dictionary) -> void:
|
||||
if _auto_map_active:
|
||||
_auto_map_draft[str(_capturing_role)] = binding.duplicate(true)
|
||||
_auto_map_index += 1
|
||||
if _auto_map_index < ControllerMappingManagerType.ROLE_ORDER.size():
|
||||
_set_capture_role(
|
||||
ControllerMappingManagerType.ROLE_ORDER[_auto_map_index]
|
||||
)
|
||||
return
|
||||
var saved: bool = _mapping_manager.replace_active_bindings(
|
||||
_auto_map_draft
|
||||
)
|
||||
_cancel_capture()
|
||||
_progress_label.text = (
|
||||
"controller mapped successfully"
|
||||
if saved else "could not save the controller mapping"
|
||||
)
|
||||
_refresh_bindings()
|
||||
return
|
||||
var role: StringName = _capturing_role
|
||||
var saved: bool = _mapping_manager.set_binding(role, binding)
|
||||
_cancel_capture()
|
||||
_progress_label.text = (
|
||||
"updated " + _mapping_manager.get_role_label(role)
|
||||
if saved else "could not save that controller input"
|
||||
)
|
||||
_refresh_bindings()
|
||||
|
||||
func _cancel_capture() -> void:
|
||||
_capturing_role = &""
|
||||
_auto_map_active = false
|
||||
_auto_map_index = -1
|
||||
_auto_map_draft.clear()
|
||||
_set_action_buttons_disabled(false)
|
||||
if _progress_label != null:
|
||||
_progress_label.text = ""
|
||||
|
||||
|
||||
func _set_action_buttons_disabled(disabled: bool) -> void:
|
||||
if _auto_map_button != null:
|
||||
_auto_map_button.disabled = disabled
|
||||
if _reset_button != null:
|
||||
_reset_button.disabled = disabled
|
||||
for button_value: Variant in _binding_buttons.values():
|
||||
var button := button_value as Button
|
||||
if button != null:
|
||||
button.disabled = disabled
|
||||
|
||||
|
||||
func _reset_mapping() -> void:
|
||||
if _mapping_manager == null:
|
||||
return
|
||||
var reset: bool = _mapping_manager.reset_active_profile()
|
||||
_progress_label.text = (
|
||||
"restored the project controller defaults"
|
||||
if reset else "could not restore controller defaults"
|
||||
)
|
||||
_refresh_bindings()
|
||||
|
||||
|
||||
func _refresh_bindings() -> void:
|
||||
if _mapping_manager == null or _controller_label == null:
|
||||
return
|
||||
_controller_label.text = _mapping_manager.get_active_controller_name()
|
||||
var bindings: Dictionary = _mapping_manager.get_active_bindings()
|
||||
for role: StringName in ControllerMappingManagerType.ROLE_ORDER:
|
||||
var button := _binding_buttons.get(role) as Button
|
||||
if button == null:
|
||||
continue
|
||||
var raw_binding: Variant = bindings.get(str(role), {})
|
||||
button.text = (
|
||||
_mapping_manager.binding_label(raw_binding as Dictionary)
|
||||
if typeof(raw_binding) == TYPE_DICTIONARY else "unmapped"
|
||||
)
|
||||
var conflict_role: StringName = _find_binding_conflict(
|
||||
role,
|
||||
raw_binding as Dictionary,
|
||||
bindings,
|
||||
)
|
||||
_apply_binding_button_style(button, not conflict_role.is_empty())
|
||||
button.tooltip_text = (
|
||||
"warning: also assigned to "
|
||||
+ _mapping_manager.get_role_label(conflict_role)
|
||||
if not conflict_role.is_empty()
|
||||
else "change " + _mapping_manager.get_role_label(role)
|
||||
)
|
||||
_reset_button.disabled = not _mapping_manager.has_custom_mapping()
|
||||
|
||||
|
||||
func _find_binding_conflict(
|
||||
role: StringName,
|
||||
binding: Dictionary,
|
||||
bindings: Dictionary,
|
||||
) -> StringName:
|
||||
if binding.is_empty():
|
||||
return &""
|
||||
for other_role: StringName in ControllerMappingManagerType.ROLE_ORDER:
|
||||
if other_role == role:
|
||||
continue
|
||||
var raw_other: Variant = bindings.get(str(other_role), {})
|
||||
if typeof(raw_other) != TYPE_DICTIONARY:
|
||||
continue
|
||||
var other := raw_other as Dictionary
|
||||
if _bindings_conflict(role, binding, other_role, other):
|
||||
return other_role
|
||||
return &""
|
||||
|
||||
|
||||
func _bindings_conflict(
|
||||
first_role: StringName,
|
||||
first: Dictionary,
|
||||
second_role: StringName,
|
||||
second: Dictionary,
|
||||
) -> bool:
|
||||
var binding_kind: String = str(first.get("kind", ""))
|
||||
if binding_kind != str(second.get("kind", "")):
|
||||
return false
|
||||
if binding_kind == "button":
|
||||
return int(first.get("button", -1)) == int(
|
||||
second.get("button", -1)
|
||||
)
|
||||
if binding_kind != "axis":
|
||||
return false
|
||||
if int(first.get("axis", -1)) != int(second.get("axis", -1)):
|
||||
return false
|
||||
var opposite_trigger_halves: bool = (
|
||||
first_role in ControllerMappingManagerType.TRIGGER_ROLES
|
||||
and second_role in ControllerMappingManagerType.TRIGGER_ROLES
|
||||
and signf(float(first.get("direction", 0.0)))
|
||||
!= signf(float(second.get("direction", 0.0)))
|
||||
)
|
||||
return not opposite_trigger_halves
|
||||
|
||||
|
||||
func _apply_binding_button_style(button: Button, has_conflict: bool) -> void:
|
||||
UtilityPageStyleType.apply_compact_ocean_button(button)
|
||||
if not has_conflict:
|
||||
return
|
||||
var colors: Dictionary = {
|
||||
&"normal": UtilityPageStyleType.OCEAN_DANGER,
|
||||
&"hover": UtilityPageStyleType.OCEAN_DANGER.lightened(0.12),
|
||||
&"pressed": UtilityPageStyleType.OCEAN_DANGER.darkened(0.12),
|
||||
&"focus": UtilityPageStyleType.OCEAN_DANGER.lightened(0.12),
|
||||
}
|
||||
for state: StringName in colors:
|
||||
var style := UtilityPageStyleType.ocean_button_style(
|
||||
colors[state] as Color
|
||||
)
|
||||
style.content_margin_left = 10.0
|
||||
style.content_margin_right = 10.0
|
||||
style.content_margin_top = 4.0
|
||||
style.content_margin_bottom = 4.0
|
||||
button.add_theme_stylebox_override(state, style)
|
||||
|
||||
|
||||
func _on_active_controller_changed(_controller_name: String) -> void:
|
||||
if not _capturing_role.is_empty():
|
||||
_cancel_capture()
|
||||
_refresh_bindings()
|
||||
1
ui/controller_mapping_panel.gd.uid
Normal file
1
ui/controller_mapping_panel.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b0rl6lgpc0h85
|
||||
|
|
@ -14,11 +14,21 @@ const SIT_SECTOR: int = 0
|
|||
const RING_RADIUS: float = 172.0
|
||||
const BUBBLE_SIZE: Vector2 = Vector2(92.0, 88.0)
|
||||
const CONTROLLER_SELECTION_DEADZONE: float = 0.35
|
||||
const ControllerMappingManagerType = preload(
|
||||
"res://settings/controller_mapping_manager.gd"
|
||||
)
|
||||
|
||||
var _buttons: Array[BubbleButton] = []
|
||||
var _is_open: bool = false
|
||||
var _selected_sector: int = SIT_SECTOR
|
||||
var _controller_selection_mode: bool = false
|
||||
var _controller_mapping_manager: ControllerMappingManagerType
|
||||
|
||||
|
||||
func setup_controller_mapping(
|
||||
mapping_manager: ControllerMappingManagerType,
|
||||
) -> void:
|
||||
_controller_mapping_manager = mapping_manager
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
@ -92,10 +102,7 @@ func _layout_bubbles() -> void:
|
|||
|
||||
|
||||
func _update_selection() -> void:
|
||||
var stick: Vector2 = Vector2(
|
||||
Input.get_joy_axis(0, JOY_AXIS_RIGHT_X),
|
||||
Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y),
|
||||
)
|
||||
var stick: Vector2 = _get_selection_stick()
|
||||
if stick.length() >= CONTROLLER_SELECTION_DEADZONE:
|
||||
_select_sector_from_offset(stick)
|
||||
return
|
||||
|
|
@ -104,6 +111,25 @@ func _update_selection() -> void:
|
|||
_update_mouse_selection()
|
||||
|
||||
|
||||
func _get_selection_stick() -> Vector2:
|
||||
if (
|
||||
_controller_mapping_manager != null
|
||||
and _controller_mapping_manager.has_custom_mapping()
|
||||
):
|
||||
return Vector2(
|
||||
_controller_mapping_manager.get_role_axis(
|
||||
ControllerMappingManagerType.ROLE_RIGHT_STICK_X
|
||||
),
|
||||
_controller_mapping_manager.get_role_axis(
|
||||
ControllerMappingManagerType.ROLE_RIGHT_STICK_Y
|
||||
),
|
||||
)
|
||||
return Vector2(
|
||||
Input.get_joy_axis(0, JOY_AXIS_RIGHT_X),
|
||||
Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y),
|
||||
)
|
||||
|
||||
|
||||
func _update_mouse_selection() -> void:
|
||||
var offset: Vector2 = get_local_mouse_position() - size * 0.5
|
||||
if offset.length() < 24.0:
|
||||
|
|
|
|||
480
ui/game_ui.gd
480
ui/game_ui.gd
|
|
@ -44,6 +44,9 @@ const SurfaceDrawingToolbarType = preload(
|
|||
const PlayerSettingsManagerType = preload(
|
||||
"res://settings/player_settings_manager.gd"
|
||||
)
|
||||
const ControllerMappingManagerType = preload(
|
||||
"res://settings/controller_mapping_manager.gd"
|
||||
)
|
||||
const WorldTimeServiceType = preload("res://world/world_time_service.gd")
|
||||
const WorldWeatherServiceType = preload(
|
||||
"res://world/world_weather_service.gd"
|
||||
|
|
@ -64,8 +67,15 @@ signal shop_backdrop_visibility_changed(is_visible: bool)
|
|||
|
||||
const VIRTUAL_MOUSE_INPUT_OWNER: StringName = &"controller_virtual_mouse"
|
||||
const VIRTUAL_MOUSE_TRIGGER_THRESHOLD: float = 0.55
|
||||
const VIRTUAL_MOUSE_TRIGGER_RELEASE_THRESHOLD: float = 0.35
|
||||
const VIRTUAL_MOUSE_STICK_DEADZONE: float = 0.18
|
||||
const VIRTUAL_MOUSE_SPEED: float = 720.0
|
||||
# Android controller mappings may expose LT on its own axis or as the negative
|
||||
# half of the same signed axis used by RT. Support both without letting the RT
|
||||
# zoom direction enter virtual-pointer mode.
|
||||
const VIRTUAL_MOUSE_TRIGGER_AXIS: JoyAxis = JOY_AXIS_TRIGGER_RIGHT
|
||||
const VIRTUAL_MOUSE_SHARED_TRIGGER_AXIS: JoyAxis = JOY_AXIS_TRIGGER_LEFT
|
||||
const VIRTUAL_MOUSE_SECONDARY_CLICK_AXIS: JoyAxis = JOY_AXIS_TRIGGER_LEFT
|
||||
|
||||
@onready var _status_label: Label = %StatusLabel
|
||||
@onready var _gameplay_transient_hud: Control = %GameplayTransientHUD
|
||||
|
|
@ -137,7 +147,12 @@ var _virtual_mouse_prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
|
|||
var _virtual_mouse_window_position: Vector2 = Vector2.ZERO
|
||||
var _virtual_mouse_button_mask: int = 0
|
||||
var _virtual_mouse_right_pressed: bool = false
|
||||
var _injecting_virtual_mouse_event: bool = false
|
||||
var _virtual_mouse_device_id: int = 0
|
||||
var _virtual_mouse_trigger_strength: float = 0.0
|
||||
var _virtual_mouse_stick: Vector2 = Vector2.ZERO
|
||||
var _virtual_mouse_trigger_rest_by_device: Dictionary[int, float] = {}
|
||||
var _shared_trigger_rest_by_device: Dictionary[int, float] = {}
|
||||
var _controller_mapping_manager: ControllerMappingManagerType
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
@ -294,21 +309,17 @@ func setup(
|
|||
set_edge_docks(
|
||||
settings_manager.current_settings.chat_dock_right,
|
||||
settings_manager.current_settings.paint_dock_right,
|
||||
settings_manager.current_settings.chat_mobile_mode,
|
||||
)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if (
|
||||
_virtual_mouse_active
|
||||
and _injecting_virtual_mouse_event
|
||||
and event is InputEventMouseMotion
|
||||
):
|
||||
_update_virtual_cursor_position(
|
||||
(event as InputEventMouseMotion).position
|
||||
)
|
||||
if _handle_virtual_mouse_input(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if _handle_controller_chat_controls(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if (
|
||||
_surface_drawing_toolbar != null
|
||||
and _surface_drawing_toolbar.owns_pointer_event(event)
|
||||
|
|
@ -378,6 +389,55 @@ func _input(event: InputEvent) -> void:
|
|||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _handle_controller_chat_controls(event: InputEvent) -> bool:
|
||||
var button_event: InputEventJoypadButton = event as InputEventJoypadButton
|
||||
if (
|
||||
button_event == null
|
||||
or not button_event.pressed
|
||||
or not _gameplay_ui_enabled
|
||||
or _system_menu_open
|
||||
or _player_menu_open
|
||||
or _shop_open
|
||||
):
|
||||
return false
|
||||
var use_mapping: bool = (
|
||||
_controller_mapping_manager != null
|
||||
and _controller_mapping_manager.has_custom_mapping()
|
||||
)
|
||||
var select_pressed: bool = (
|
||||
_controller_mapping_manager.event_matches_role(
|
||||
event, ControllerMappingManagerType.ROLE_SELECT
|
||||
)
|
||||
if use_mapping
|
||||
else button_event.button_index == JOY_BUTTON_BACK
|
||||
)
|
||||
var focus_pressed: bool = (
|
||||
_controller_mapping_manager.event_matches_role(
|
||||
event, ControllerMappingManagerType.ROLE_LB
|
||||
)
|
||||
if use_mapping
|
||||
else button_event.button_index == JOY_BUTTON_LEFT_SHOULDER
|
||||
)
|
||||
var accept_pressed: bool = (
|
||||
_controller_mapping_manager.event_matches_role(
|
||||
event, ControllerMappingManagerType.ROLE_A
|
||||
)
|
||||
if use_mapping
|
||||
else button_event.button_index == JOY_BUTTON_A
|
||||
)
|
||||
if select_pressed:
|
||||
# Handle Select before focused LineEdit controls consume it. Select owns
|
||||
# chat visibility while LB only returns input ownership to the world.
|
||||
_chat_ui.toggle_chat()
|
||||
return true
|
||||
if focus_pressed:
|
||||
_chat_ui.toggle_focus()
|
||||
return true
|
||||
if accept_pressed:
|
||||
return _chat_ui.request_virtual_keyboard()
|
||||
return false
|
||||
|
||||
|
||||
func _on_emote_selected(emote_id: StringName) -> void:
|
||||
if emote_id == &"sit" and _player != null:
|
||||
_player.toggle_sitting()
|
||||
|
|
@ -404,24 +464,59 @@ func _on_quick_action_selected(action_id: StringName) -> void:
|
|||
|
||||
|
||||
func _handle_virtual_mouse_input(event: InputEvent) -> bool:
|
||||
if (
|
||||
_controller_mapping_manager != null
|
||||
and _controller_mapping_manager.has_custom_mapping()
|
||||
):
|
||||
return _handle_mapped_virtual_mouse_input(event)
|
||||
var motion_event: InputEventJoypadMotion = event as InputEventJoypadMotion
|
||||
if motion_event != null:
|
||||
if motion_event.axis == JOY_AXIS_TRIGGER_LEFT:
|
||||
if motion_event.axis in [
|
||||
VIRTUAL_MOUSE_TRIGGER_AXIS,
|
||||
VIRTUAL_MOUSE_SHARED_TRIGGER_AXIS,
|
||||
]:
|
||||
if (
|
||||
_virtual_mouse_active
|
||||
and motion_event.device != _virtual_mouse_device_id
|
||||
):
|
||||
return false
|
||||
_sample_trigger_rest_values(motion_event.device)
|
||||
_virtual_mouse_trigger_strength = (
|
||||
_virtual_mouse_strength_for_device(motion_event.device)
|
||||
)
|
||||
var was_active: bool = _virtual_mouse_active
|
||||
if motion_event.axis_value >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD:
|
||||
if (
|
||||
_virtual_mouse_trigger_strength
|
||||
>= VIRTUAL_MOUSE_TRIGGER_THRESHOLD
|
||||
):
|
||||
if not _virtual_mouse_active and _can_start_virtual_mouse():
|
||||
_begin_virtual_mouse()
|
||||
elif _virtual_mouse_active:
|
||||
_begin_virtual_mouse(motion_event.device)
|
||||
elif (
|
||||
_virtual_mouse_active
|
||||
and _virtual_mouse_trigger_strength
|
||||
< VIRTUAL_MOUSE_TRIGGER_RELEASE_THRESHOLD
|
||||
):
|
||||
_end_virtual_mouse()
|
||||
elif _virtual_mouse_active:
|
||||
_set_virtual_mouse_button(
|
||||
MOUSE_BUTTON_RIGHT,
|
||||
_secondary_click_strength_for_device(
|
||||
motion_event.device
|
||||
) >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD,
|
||||
)
|
||||
return was_active or _virtual_mouse_active
|
||||
if (
|
||||
_virtual_mouse_active
|
||||
and motion_event.axis == JOY_AXIS_TRIGGER_RIGHT
|
||||
and motion_event.device == _virtual_mouse_device_id
|
||||
and motion_event.axis in [
|
||||
JOY_AXIS_RIGHT_X,
|
||||
JOY_AXIS_RIGHT_Y,
|
||||
]
|
||||
):
|
||||
_set_virtual_mouse_button(
|
||||
MOUSE_BUTTON_RIGHT,
|
||||
motion_event.axis_value >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD,
|
||||
)
|
||||
if motion_event.axis == JOY_AXIS_RIGHT_X:
|
||||
_virtual_mouse_stick.x = motion_event.axis_value
|
||||
else:
|
||||
_virtual_mouse_stick.y = motion_event.axis_value
|
||||
return true
|
||||
var button_event: InputEventJoypadButton = event as InputEventJoypadButton
|
||||
if (
|
||||
|
|
@ -429,34 +524,124 @@ func _handle_virtual_mouse_input(event: InputEvent) -> bool:
|
|||
and button_event.button_index == JOY_BUTTON_RIGHT_SHOULDER
|
||||
and (
|
||||
_virtual_mouse_active
|
||||
or Input.get_joy_axis(0, JOY_AXIS_TRIGGER_LEFT)
|
||||
>= VIRTUAL_MOUSE_TRIGGER_THRESHOLD
|
||||
or _virtual_mouse_trigger_strength
|
||||
>= VIRTUAL_MOUSE_TRIGGER_THRESHOLD
|
||||
)
|
||||
):
|
||||
if not _virtual_mouse_active and _can_start_virtual_mouse():
|
||||
_begin_virtual_mouse()
|
||||
_begin_virtual_mouse(button_event.device)
|
||||
if _virtual_mouse_active:
|
||||
_set_virtual_mouse_button(MOUSE_BUTTON_LEFT, button_event.pressed)
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _handle_mapped_virtual_mouse_input(event: InputEvent) -> bool:
|
||||
var uses_activation: bool = _controller_mapping_manager.event_uses_role(
|
||||
event,
|
||||
ControllerMappingManagerType.ROLE_LT,
|
||||
)
|
||||
if uses_activation:
|
||||
var was_active: bool = _virtual_mouse_active
|
||||
_virtual_mouse_trigger_strength = (
|
||||
_controller_mapping_manager.get_role_strength(
|
||||
ControllerMappingManagerType.ROLE_LT
|
||||
)
|
||||
)
|
||||
if (
|
||||
_virtual_mouse_trigger_strength
|
||||
>= VIRTUAL_MOUSE_TRIGGER_THRESHOLD
|
||||
and not _virtual_mouse_active
|
||||
and _can_start_virtual_mouse()
|
||||
):
|
||||
_begin_virtual_mouse(
|
||||
_controller_mapping_manager.get_active_device_id()
|
||||
)
|
||||
elif (
|
||||
_virtual_mouse_active
|
||||
and _virtual_mouse_trigger_strength
|
||||
< VIRTUAL_MOUSE_TRIGGER_RELEASE_THRESHOLD
|
||||
):
|
||||
_end_virtual_mouse()
|
||||
return was_active or _virtual_mouse_active
|
||||
if not _virtual_mouse_active:
|
||||
return false
|
||||
if (
|
||||
_controller_mapping_manager.event_uses_role(
|
||||
event,
|
||||
ControllerMappingManagerType.ROLE_RIGHT_STICK_X,
|
||||
)
|
||||
or _controller_mapping_manager.event_uses_role(
|
||||
event,
|
||||
ControllerMappingManagerType.ROLE_RIGHT_STICK_Y,
|
||||
)
|
||||
):
|
||||
_virtual_mouse_stick = _mapped_virtual_mouse_stick()
|
||||
return true
|
||||
var button_event := event as InputEventJoypadButton
|
||||
if button_event != null and _controller_mapping_manager.event_uses_role(
|
||||
event,
|
||||
ControllerMappingManagerType.ROLE_RB,
|
||||
):
|
||||
_set_virtual_mouse_button(MOUSE_BUTTON_LEFT, button_event.pressed)
|
||||
return true
|
||||
if _controller_mapping_manager.event_uses_role(
|
||||
event,
|
||||
ControllerMappingManagerType.ROLE_RT,
|
||||
):
|
||||
_set_virtual_mouse_button(
|
||||
MOUSE_BUTTON_RIGHT,
|
||||
_controller_mapping_manager.get_role_strength(
|
||||
ControllerMappingManagerType.ROLE_RT
|
||||
) >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD,
|
||||
)
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _mapped_virtual_mouse_stick() -> Vector2:
|
||||
return Vector2(
|
||||
_controller_mapping_manager.get_role_axis(
|
||||
ControllerMappingManagerType.ROLE_RIGHT_STICK_X
|
||||
),
|
||||
_controller_mapping_manager.get_role_axis(
|
||||
ControllerMappingManagerType.ROLE_RIGHT_STICK_Y
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
func _can_start_virtual_mouse() -> bool:
|
||||
return (
|
||||
_gameplay_ui_enabled
|
||||
and not _showcase_active
|
||||
and not _system_menu_open
|
||||
and not _player_menu_open
|
||||
and not _shop_open
|
||||
and not _chat_input_open
|
||||
and _player != null
|
||||
and _fishing_spot != null
|
||||
and _fishing_spot.can_use_surface_drawing()
|
||||
and _fishing_spot.can_open_system_menu()
|
||||
and not _emote_radial_menu.is_open()
|
||||
and not _quick_radial_menu.is_open()
|
||||
)
|
||||
|
||||
|
||||
func _begin_virtual_mouse() -> void:
|
||||
func _begin_virtual_mouse(device_id: int) -> void:
|
||||
if _virtual_mouse_active:
|
||||
return
|
||||
_virtual_mouse_active = true
|
||||
_virtual_mouse_device_id = maxi(device_id, 0)
|
||||
_virtual_mouse_stick = (
|
||||
_mapped_virtual_mouse_stick()
|
||||
if (
|
||||
_controller_mapping_manager != null
|
||||
and _controller_mapping_manager.has_custom_mapping()
|
||||
)
|
||||
else Vector2(
|
||||
Input.get_joy_axis(_virtual_mouse_device_id, JOY_AXIS_RIGHT_X),
|
||||
Input.get_joy_axis(_virtual_mouse_device_id, JOY_AXIS_RIGHT_Y),
|
||||
)
|
||||
)
|
||||
_virtual_mouse_prior_camera_input_enabled = (
|
||||
_player.is_camera_input_enabled()
|
||||
)
|
||||
|
|
@ -470,8 +655,11 @@ func _begin_virtual_mouse() -> void:
|
|||
Input.mouse_mode = Input.MOUSE_MODE_HIDDEN
|
||||
_virtual_mouse_button_mask = 0
|
||||
_virtual_mouse_right_pressed = false
|
||||
_virtual_mouse_window_position = Vector2(get_window().size) * 0.5
|
||||
_virtual_mouse_window_position = _clamp_virtual_mouse_window_position(
|
||||
Vector2(get_window().size) * 0.5
|
||||
)
|
||||
_controller_virtual_cursor.visible = true
|
||||
_update_virtual_cursor_position(_virtual_mouse_window_position)
|
||||
_emit_virtual_mouse_motion(Vector2.ZERO)
|
||||
|
||||
|
||||
|
|
@ -483,6 +671,8 @@ func _end_virtual_mouse() -> void:
|
|||
if _virtual_mouse_right_pressed:
|
||||
_set_virtual_mouse_button(MOUSE_BUTTON_RIGHT, false)
|
||||
_virtual_mouse_active = false
|
||||
_virtual_mouse_trigger_strength = 0.0
|
||||
_virtual_mouse_stick = Vector2.ZERO
|
||||
_controller_virtual_cursor.visible = false
|
||||
if _fishing_spot != null:
|
||||
_fishing_spot.set_local_menu_input_suppressed(
|
||||
|
|
@ -500,15 +690,12 @@ func _update_virtual_mouse(delta: float) -> void:
|
|||
if not _virtual_mouse_active:
|
||||
return
|
||||
if (
|
||||
Input.get_joy_axis(0, JOY_AXIS_TRIGGER_LEFT)
|
||||
< VIRTUAL_MOUSE_TRIGGER_THRESHOLD
|
||||
_virtual_mouse_trigger_strength
|
||||
< VIRTUAL_MOUSE_TRIGGER_RELEASE_THRESHOLD
|
||||
):
|
||||
_end_virtual_mouse()
|
||||
return
|
||||
var stick: Vector2 = Vector2(
|
||||
Input.get_joy_axis(0, JOY_AXIS_RIGHT_X),
|
||||
Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y),
|
||||
)
|
||||
var stick: Vector2 = _virtual_mouse_stick
|
||||
var stick_length: float = stick.length()
|
||||
if stick_length <= VIRTUAL_MOUSE_STICK_DEADZONE:
|
||||
return
|
||||
|
|
@ -526,22 +713,166 @@ func _update_virtual_mouse(delta: float) -> void:
|
|||
* display_scale
|
||||
* delta
|
||||
)
|
||||
var window_size: Vector2 = Vector2(get_window().size)
|
||||
_virtual_mouse_window_position = Vector2(
|
||||
clampf(
|
||||
_virtual_mouse_window_position.x + relative_motion.x,
|
||||
0.0,
|
||||
window_size.x,
|
||||
),
|
||||
clampf(
|
||||
_virtual_mouse_window_position.y + relative_motion.y,
|
||||
0.0,
|
||||
window_size.y,
|
||||
),
|
||||
_virtual_mouse_window_position = _clamp_virtual_mouse_window_position(
|
||||
_virtual_mouse_window_position + relative_motion
|
||||
)
|
||||
_update_virtual_cursor_position(_virtual_mouse_window_position)
|
||||
_emit_virtual_mouse_motion(relative_motion)
|
||||
|
||||
|
||||
func _poll_virtual_mouse_controller_state() -> void:
|
||||
if (
|
||||
_controller_mapping_manager != null
|
||||
and _controller_mapping_manager.has_custom_mapping()
|
||||
):
|
||||
_poll_mapped_virtual_mouse_controller_state()
|
||||
return
|
||||
var device_ids: Array[int] = Input.get_connected_joypads()
|
||||
# Some Android controller backends deliver device-0 axes without including
|
||||
# that device in get_connected_joypads(). Player camera input already uses
|
||||
# this same primary-device fallback.
|
||||
if device_ids.is_empty():
|
||||
device_ids.append(0)
|
||||
for device_id: int in device_ids:
|
||||
_sample_trigger_rest_values(device_id)
|
||||
if _virtual_mouse_active:
|
||||
if not device_ids.has(_virtual_mouse_device_id):
|
||||
_end_virtual_mouse()
|
||||
return
|
||||
_virtual_mouse_trigger_strength = _virtual_mouse_strength_for_device(
|
||||
_virtual_mouse_device_id
|
||||
)
|
||||
_set_virtual_mouse_button(
|
||||
MOUSE_BUTTON_RIGHT,
|
||||
_secondary_click_strength_for_device(
|
||||
_virtual_mouse_device_id
|
||||
) >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD,
|
||||
)
|
||||
_virtual_mouse_stick = Vector2(
|
||||
Input.get_joy_axis(
|
||||
_virtual_mouse_device_id,
|
||||
JOY_AXIS_RIGHT_X,
|
||||
),
|
||||
Input.get_joy_axis(
|
||||
_virtual_mouse_device_id,
|
||||
JOY_AXIS_RIGHT_Y,
|
||||
),
|
||||
)
|
||||
return
|
||||
if not _can_start_virtual_mouse():
|
||||
return
|
||||
for device_id: int in device_ids:
|
||||
var trigger_strength: float = _virtual_mouse_strength_for_device(
|
||||
device_id
|
||||
)
|
||||
if trigger_strength < VIRTUAL_MOUSE_TRIGGER_THRESHOLD:
|
||||
continue
|
||||
_virtual_mouse_trigger_strength = trigger_strength
|
||||
_begin_virtual_mouse(device_id)
|
||||
return
|
||||
|
||||
|
||||
func _poll_mapped_virtual_mouse_controller_state() -> void:
|
||||
var trigger_strength: float = _controller_mapping_manager.get_role_strength(
|
||||
ControllerMappingManagerType.ROLE_LT
|
||||
)
|
||||
if _virtual_mouse_active:
|
||||
_virtual_mouse_trigger_strength = trigger_strength
|
||||
if trigger_strength < VIRTUAL_MOUSE_TRIGGER_RELEASE_THRESHOLD:
|
||||
_end_virtual_mouse()
|
||||
return
|
||||
_virtual_mouse_stick = _mapped_virtual_mouse_stick()
|
||||
_set_virtual_mouse_button(
|
||||
MOUSE_BUTTON_RIGHT,
|
||||
_controller_mapping_manager.get_role_strength(
|
||||
ControllerMappingManagerType.ROLE_RT
|
||||
) >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD,
|
||||
)
|
||||
return
|
||||
if (
|
||||
trigger_strength >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD
|
||||
and _can_start_virtual_mouse()
|
||||
):
|
||||
_virtual_mouse_trigger_strength = trigger_strength
|
||||
_begin_virtual_mouse(
|
||||
_controller_mapping_manager.get_active_device_id()
|
||||
)
|
||||
|
||||
|
||||
func _sample_trigger_rest_values(device_id: int) -> void:
|
||||
if not _virtual_mouse_trigger_rest_by_device.has(device_id):
|
||||
_virtual_mouse_trigger_rest_by_device[device_id] = Input.get_joy_axis(
|
||||
device_id,
|
||||
VIRTUAL_MOUSE_TRIGGER_AXIS,
|
||||
)
|
||||
if not _shared_trigger_rest_by_device.has(device_id):
|
||||
_shared_trigger_rest_by_device[device_id] = Input.get_joy_axis(
|
||||
device_id,
|
||||
VIRTUAL_MOUSE_SHARED_TRIGGER_AXIS,
|
||||
)
|
||||
|
||||
|
||||
func _virtual_mouse_strength_for_device(device_id: int) -> float:
|
||||
_sample_trigger_rest_values(device_id)
|
||||
var dedicated_rest: float = (
|
||||
_virtual_mouse_trigger_rest_by_device[device_id]
|
||||
)
|
||||
var shared_rest: float = _shared_trigger_rest_by_device[device_id]
|
||||
var dedicated_strength: float = normalized_trigger_strength(
|
||||
Input.get_joy_axis(device_id, VIRTUAL_MOUSE_TRIGGER_AXIS),
|
||||
dedicated_rest,
|
||||
)
|
||||
var shared_negative_strength: float = directional_trigger_strength(
|
||||
Input.get_joy_axis(device_id, VIRTUAL_MOUSE_SHARED_TRIGGER_AXIS),
|
||||
shared_rest,
|
||||
-1.0,
|
||||
)
|
||||
return maxf(dedicated_strength, shared_negative_strength)
|
||||
|
||||
|
||||
func _secondary_click_strength_for_device(device_id: int) -> float:
|
||||
_sample_trigger_rest_values(device_id)
|
||||
return directional_trigger_strength(
|
||||
Input.get_joy_axis(device_id, VIRTUAL_MOUSE_SECONDARY_CLICK_AXIS),
|
||||
_shared_trigger_rest_by_device[device_id],
|
||||
1.0,
|
||||
)
|
||||
|
||||
|
||||
static func normalized_trigger_strength(
|
||||
axis_value: float,
|
||||
resting_value: float,
|
||||
) -> float:
|
||||
var negative_travel: float = absf(-1.0 - resting_value)
|
||||
var positive_travel: float = absf(1.0 - resting_value)
|
||||
var available_travel: float = maxf(negative_travel, positive_travel)
|
||||
if available_travel <= 0.001:
|
||||
return 0.0
|
||||
return clampf(
|
||||
absf(axis_value - resting_value) / available_travel,
|
||||
0.0,
|
||||
1.0,
|
||||
)
|
||||
|
||||
|
||||
static func directional_trigger_strength(
|
||||
axis_value: float,
|
||||
resting_value: float,
|
||||
direction: float,
|
||||
) -> float:
|
||||
var normalized_direction: float = signf(direction)
|
||||
if is_zero_approx(normalized_direction):
|
||||
return 0.0
|
||||
var endpoint: float = normalized_direction
|
||||
var available_travel: float = absf(endpoint - resting_value)
|
||||
if available_travel <= 0.001:
|
||||
return 0.0
|
||||
var directed_travel: float = (
|
||||
(axis_value - resting_value) * normalized_direction
|
||||
)
|
||||
return clampf(directed_travel / available_travel, 0.0, 1.0)
|
||||
|
||||
|
||||
func _set_virtual_mouse_button(button: MouseButton, pressed: bool) -> void:
|
||||
var mask: int = (
|
||||
MOUSE_BUTTON_MASK_LEFT
|
||||
|
|
@ -577,16 +908,40 @@ func _emit_virtual_mouse_motion(relative_motion: Vector2) -> void:
|
|||
|
||||
|
||||
func _parse_virtual_mouse_event(event: InputEventMouse) -> void:
|
||||
_injecting_virtual_mouse_event = true
|
||||
Input.parse_input_event(event)
|
||||
_injecting_virtual_mouse_event = false
|
||||
|
||||
|
||||
func _update_virtual_cursor_position(viewport_position: Vector2) -> void:
|
||||
var root_transform: Transform2D = _ui_root.get_global_transform_with_canvas()
|
||||
_controller_virtual_cursor.set_pointer_position(
|
||||
root_transform.affine_inverse() * viewport_position
|
||||
var output_scale: float = UIReferencePresentationType.get_scale(
|
||||
Vector2(get_window().size)
|
||||
)
|
||||
_controller_virtual_cursor.set_pointer_position(
|
||||
viewport_position / output_scale
|
||||
)
|
||||
|
||||
|
||||
func _clamp_virtual_mouse_window_position(
|
||||
window_position: Vector2,
|
||||
) -> Vector2:
|
||||
var bounds: Rect2 = get_virtual_mouse_window_bounds(
|
||||
Vector2(get_window().size)
|
||||
)
|
||||
return Vector2(
|
||||
clampf(window_position.x, bounds.position.x, bounds.end.x),
|
||||
clampf(window_position.y, bounds.position.y, bounds.end.y),
|
||||
)
|
||||
|
||||
|
||||
static func get_virtual_mouse_window_bounds(window_size: Vector2) -> Rect2:
|
||||
var output_scale: float = UIReferencePresentationType.get_scale(window_size)
|
||||
var cursor_margin: Vector2 = (
|
||||
ControllerVirtualCursorType.CURSOR_SIZE * output_scale * 0.5
|
||||
)
|
||||
var bounds_size: Vector2 = Vector2(
|
||||
maxf(window_size.x - cursor_margin.x * 2.0, 0.0),
|
||||
maxf(window_size.y - cursor_margin.y * 2.0, 0.0),
|
||||
)
|
||||
return Rect2(cursor_margin, bounds_size)
|
||||
|
||||
|
||||
func _toggle_surface_drawing() -> void:
|
||||
|
|
@ -619,7 +974,29 @@ func setup_data_and_identity(
|
|||
)
|
||||
|
||||
|
||||
func setup_controller_mapping(
|
||||
mapping_manager: ControllerMappingManagerType,
|
||||
) -> void:
|
||||
_controller_mapping_manager = mapping_manager
|
||||
_player.set_controller_mapping_manager(_controller_mapping_manager)
|
||||
_emote_radial_menu.setup_controller_mapping(_controller_mapping_manager)
|
||||
_quick_radial_menu.setup_controller_mapping(_controller_mapping_manager)
|
||||
_player_menu.setup_controller_mapping(_controller_mapping_manager)
|
||||
for panel: SettingsPanelType in [
|
||||
_title_settings_panel, _pause_settings_panel
|
||||
]:
|
||||
panel.setup_controller_mapping(_controller_mapping_manager)
|
||||
|
||||
|
||||
func is_controller_mapping_capturing() -> bool:
|
||||
return (
|
||||
_title_settings_panel.is_controller_mapping_capturing()
|
||||
or _pause_settings_panel.is_controller_mapping_capturing()
|
||||
)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
_poll_virtual_mouse_controller_state()
|
||||
_update_virtual_mouse(delta)
|
||||
_update_experience_bubble_position()
|
||||
if _item_effects == null or not _gameplay_ui_enabled:
|
||||
|
|
@ -753,8 +1130,13 @@ func set_effective_ui_pixel_size(pixel_size: int) -> void:
|
|||
_pause_settings_panel.set_effective_ui_pixel_size(pixel_size)
|
||||
|
||||
|
||||
func set_edge_docks(chat_dock_right: bool, paint_dock_right: bool) -> void:
|
||||
func set_edge_docks(
|
||||
chat_dock_right: bool,
|
||||
paint_dock_right: bool,
|
||||
chat_mobile_mode: bool,
|
||||
) -> void:
|
||||
_chat_ui.set_dock_right(chat_dock_right)
|
||||
_chat_ui.set_mobile_mode(chat_mobile_mode)
|
||||
_surface_drawing_toolbar.set_dock_right(paint_dock_right)
|
||||
|
||||
|
||||
|
|
@ -1131,6 +1513,8 @@ func _update_experience_bubble_position() -> void:
|
|||
|
||||
func _on_player_menu_visibility_changed(is_open: bool) -> void:
|
||||
_player_menu_open = is_open
|
||||
if is_open:
|
||||
_end_virtual_mouse()
|
||||
if is_open and _surface_drawing != null:
|
||||
_surface_drawing.deactivate()
|
||||
_gameplay_transient_hud.visible = _gameplay_ui_enabled and not is_open
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ const PlayerCoolerCapacityType = preload(
|
|||
const PlayerExperienceType = preload(
|
||||
"res://progression/player_experience.gd"
|
||||
)
|
||||
const ControllerMappingManagerType = preload(
|
||||
"res://settings/controller_mapping_manager.gd"
|
||||
)
|
||||
const FishBatchSelectionType = preload(
|
||||
"res://ui/fish_batch_selection.gd"
|
||||
)
|
||||
|
|
@ -280,6 +283,7 @@ var _network_mail_service: NetworkMailService
|
|||
var _reservations: PlayerAssetReservationService
|
||||
var _network_profile_service: NetworkProfileService
|
||||
var _network_player_list: NetworkPlayerListService
|
||||
var _controller_mapping_manager: ControllerMappingManagerType
|
||||
var _default_buyer: FishBuyerProfileType
|
||||
var _sale_buyer_override: FishBuyerProfileType
|
||||
var _shop_cooler_context_active: bool = false
|
||||
|
|
@ -564,9 +568,19 @@ func setup(
|
|||
_refresh_all()
|
||||
|
||||
|
||||
func setup_controller_mapping(
|
||||
mapping_manager: ControllerMappingManagerType,
|
||||
) -> void:
|
||||
_controller_mapping_manager = mapping_manager
|
||||
_profile_page.setup_controller_mapping(_controller_mapping_manager)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.echo:
|
||||
return
|
||||
if _handle_controller_page_switch(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if _handle_direct_page_shortcut(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
|
|
@ -585,6 +599,71 @@ func _input(event: InputEvent) -> void:
|
|||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _handle_controller_page_switch(event: InputEvent) -> bool:
|
||||
var button_event: InputEventJoypadButton = event as InputEventJoypadButton
|
||||
var use_mapping: bool = (
|
||||
_controller_mapping_manager != null
|
||||
and _controller_mapping_manager.has_custom_mapping()
|
||||
)
|
||||
var uses_left_bumper: bool = (
|
||||
_controller_mapping_manager.event_uses_role(
|
||||
event, ControllerMappingManagerType.ROLE_LB
|
||||
)
|
||||
if use_mapping
|
||||
else (
|
||||
button_event != null
|
||||
and button_event.button_index == JOY_BUTTON_LEFT_SHOULDER
|
||||
)
|
||||
)
|
||||
var uses_right_bumper: bool = (
|
||||
_controller_mapping_manager.event_uses_role(
|
||||
event, ControllerMappingManagerType.ROLE_RB
|
||||
)
|
||||
if use_mapping
|
||||
else (
|
||||
button_event != null
|
||||
and button_event.button_index == JOY_BUTTON_RIGHT_SHOULDER
|
||||
)
|
||||
)
|
||||
if (
|
||||
button_event == null
|
||||
or not (uses_left_bumper or uses_right_bumper)
|
||||
or not visible
|
||||
):
|
||||
return false
|
||||
if not button_event.pressed:
|
||||
return true
|
||||
# Shoulder input belongs to the Player Menu while it is visible, even when
|
||||
# a transition or modal temporarily prevents changing pages. This keeps LB
|
||||
# from opening Chat and RB from leaking into gameplay behind the menu.
|
||||
if (
|
||||
_transitioning
|
||||
or _page_transitioning
|
||||
or _sale_confirmation.visible
|
||||
or get_viewport().gui_is_dragging()
|
||||
):
|
||||
return true
|
||||
var sections: Array[Section] = [
|
||||
_last_inventory_section,
|
||||
Section.LOGBOOK,
|
||||
Section.NET,
|
||||
Section.MAIL,
|
||||
Section.PROFILE,
|
||||
Section.PLAYERS,
|
||||
]
|
||||
var current_index: int = 0
|
||||
if not _is_inventory_section(_current_section):
|
||||
current_index = sections.find(_current_section)
|
||||
if current_index < 0:
|
||||
current_index = 0
|
||||
var direction: int = (
|
||||
-1 if uses_left_bumper else 1
|
||||
)
|
||||
var next_index: int = wrapi(current_index + direction, 0, sections.size())
|
||||
_show_section(sections[next_index])
|
||||
return true
|
||||
|
||||
|
||||
func _handle_direct_page_shortcut(event: InputEvent) -> bool:
|
||||
var key_event := event as InputEventKey
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ class_name ProfilePage
|
|||
extends Control
|
||||
|
||||
const CHECK_DEBOUNCE_SECONDS: float = 0.4
|
||||
const ControllerMappingManagerType = preload(
|
||||
"res://settings/controller_mapping_manager.gd"
|
||||
)
|
||||
|
||||
var _service: NetworkProfileService
|
||||
var _experience: PlayerExperience
|
||||
|
|
@ -70,12 +73,20 @@ func setup(
|
|||
)
|
||||
|
||||
|
||||
func setup_controller_mapping(
|
||||
mapping_manager: ControllerMappingManagerType,
|
||||
) -> void:
|
||||
if _preview != null:
|
||||
_preview.setup_controller_mapping(mapping_manager)
|
||||
|
||||
|
||||
func activate() -> void:
|
||||
visible = true
|
||||
UtilityPageStyle.animate_in(self)
|
||||
if _service != null:
|
||||
_load_persisted()
|
||||
_name_edit.grab_focus()
|
||||
# Keep controller page switches on the Profile navigation bubble. Focusing
|
||||
# the name field here summons the Android keyboard during LB/RB traversal.
|
||||
|
||||
|
||||
func deactivate() -> void:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
class_name ProfilePreview
|
||||
extends SubViewportContainer
|
||||
|
||||
const ControllerMappingManagerType = preload(
|
||||
"res://settings/controller_mapping_manager.gd"
|
||||
)
|
||||
|
||||
@export_range(0.1, 2.0, 0.05) var drag_sensitivity: float = 0.012
|
||||
@export_range(0.1, 4.0, 0.1) var keyboard_speed: float = 1.8
|
||||
|
||||
|
|
@ -8,6 +12,13 @@ extends SubViewportContainer
|
|||
|
||||
var _dragging: bool = false
|
||||
var _visuals: Node3D
|
||||
var _controller_mapping_manager: ControllerMappingManagerType
|
||||
|
||||
|
||||
func setup_controller_mapping(
|
||||
mapping_manager: ControllerMappingManagerType,
|
||||
) -> void:
|
||||
_controller_mapping_manager = mapping_manager
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
@ -36,7 +47,16 @@ func _process(delta: float) -> void:
|
|||
if not has_focus():
|
||||
return
|
||||
var axis := Input.get_axis("ui_left", "ui_right")
|
||||
var right_stick := Input.get_joy_axis(0, JOY_AXIS_RIGHT_X)
|
||||
var right_stick: float = (
|
||||
_controller_mapping_manager.get_role_axis(
|
||||
ControllerMappingManagerType.ROLE_RIGHT_STICK_X
|
||||
)
|
||||
if (
|
||||
_controller_mapping_manager != null
|
||||
and _controller_mapping_manager.has_custom_mapping()
|
||||
)
|
||||
else Input.get_joy_axis(0, JOY_AXIS_RIGHT_X)
|
||||
)
|
||||
if absf(right_stick) > 0.2:
|
||||
axis = right_stick
|
||||
if absf(axis) > 0.1:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ const SECTOR_COUNT: int = 8
|
|||
const RING_RADIUS: float = 172.0
|
||||
const BUBBLE_SIZE: Vector2 = Vector2(92.0, 88.0)
|
||||
const CONTROLLER_SELECTION_DEADZONE: float = 0.35
|
||||
const ControllerMappingManagerType = preload(
|
||||
"res://settings/controller_mapping_manager.gd"
|
||||
)
|
||||
const ACTIONS: Array[StringName] = [
|
||||
&"stuff",
|
||||
&"logbook",
|
||||
|
|
@ -38,6 +41,13 @@ var _buttons: Array[BubbleButton] = []
|
|||
var _is_open: bool = false
|
||||
var _selected_sector: int = 0
|
||||
var _controller_selection_mode: bool = false
|
||||
var _controller_mapping_manager: ControllerMappingManagerType
|
||||
|
||||
|
||||
func setup_controller_mapping(
|
||||
mapping_manager: ControllerMappingManagerType,
|
||||
) -> void:
|
||||
_controller_mapping_manager = mapping_manager
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
@ -114,10 +124,7 @@ func _layout_bubbles() -> void:
|
|||
|
||||
|
||||
func _update_selection() -> void:
|
||||
var stick: Vector2 = Vector2(
|
||||
Input.get_joy_axis(0, JOY_AXIS_RIGHT_X),
|
||||
Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y),
|
||||
)
|
||||
var stick: Vector2 = _get_selection_stick()
|
||||
if stick.length() >= CONTROLLER_SELECTION_DEADZONE:
|
||||
_select_sector_from_offset(stick)
|
||||
return
|
||||
|
|
@ -126,6 +133,25 @@ func _update_selection() -> void:
|
|||
_update_mouse_selection()
|
||||
|
||||
|
||||
func _get_selection_stick() -> Vector2:
|
||||
if (
|
||||
_controller_mapping_manager != null
|
||||
and _controller_mapping_manager.has_custom_mapping()
|
||||
):
|
||||
return Vector2(
|
||||
_controller_mapping_manager.get_role_axis(
|
||||
ControllerMappingManagerType.ROLE_RIGHT_STICK_X
|
||||
),
|
||||
_controller_mapping_manager.get_role_axis(
|
||||
ControllerMappingManagerType.ROLE_RIGHT_STICK_Y
|
||||
),
|
||||
)
|
||||
return Vector2(
|
||||
Input.get_joy_axis(0, JOY_AXIS_RIGHT_X),
|
||||
Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y),
|
||||
)
|
||||
|
||||
|
||||
func _update_mouse_selection() -> void:
|
||||
var offset: Vector2 = get_local_mouse_position() - size * 0.5
|
||||
if offset.length() < 24.0:
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ const PIXELATION_NAMES: PackedStringArray = [
|
|||
const SettingsManagerType = preload(
|
||||
"res://settings/player_settings_manager.gd"
|
||||
)
|
||||
const ControllerMappingManagerType = preload(
|
||||
"res://settings/controller_mapping_manager.gd"
|
||||
)
|
||||
const ControllerMappingPanelType = preload(
|
||||
"res://ui/controller_mapping_panel.gd"
|
||||
)
|
||||
|
||||
signal applied
|
||||
signal closed
|
||||
|
|
@ -42,6 +48,7 @@ enum PresentationMode {
|
|||
@onready var _world_value: BubbleButton = %WorldValue
|
||||
@onready var _ui_value: BubbleButton = %UIValue
|
||||
@onready var _chat_dock: BubbleButton = %ChatDock
|
||||
@onready var _chat_mode: BubbleButton = %ChatMode
|
||||
@onready var _paint_dock: BubbleButton = %PaintDock
|
||||
@onready var _mouse_value: BubbleButton = %MouseValue
|
||||
@onready var _controller_value: BubbleButton = %ControllerValue
|
||||
|
|
@ -84,6 +91,8 @@ var _identity_backups: IdentityBackupService
|
|||
var _player_identity: PlayerIdentityStore
|
||||
var _host_identity: HostIdentityStore
|
||||
var _interface_fonts: InterfaceFontController
|
||||
var _controller_mapping_manager: ControllerMappingManagerType
|
||||
var _controller_mapping_panel: ControllerMappingPanelType
|
||||
var _data_folder_dialog: FileDialog
|
||||
var _backup_file_dialog: FileDialog
|
||||
var _export_file_dialog: FileDialog
|
||||
|
|
@ -114,6 +123,7 @@ func _ready() -> void:
|
|||
%RootBackButton.pressed.connect(close_panel)
|
||||
%DisplayBackButton.pressed.connect(handle_back)
|
||||
%ControlsBackButton.pressed.connect(handle_back)
|
||||
%ControllerMapping.pressed.connect(_open_controller_mapping)
|
||||
%AccessibilityBackButton.pressed.connect(handle_back)
|
||||
%DataBackButton.pressed.connect(handle_back)
|
||||
%RootBackButton.gui_input.connect(_on_back_bubble_gui_input)
|
||||
|
|
@ -136,6 +146,7 @@ func _ready() -> void:
|
|||
_set_ui_pixelation.bind(index + 1)
|
||||
)
|
||||
_chat_dock.pressed.connect(_toggle_chat_dock)
|
||||
_chat_mode.pressed.connect(_toggle_chat_mode)
|
||||
_paint_dock.pressed.connect(_toggle_paint_dock)
|
||||
%MouseDecrease.pressed.connect(_adjust_mouse_sensitivity.bind(-1))
|
||||
%MouseIncrease.pressed.connect(_adjust_mouse_sensitivity.bind(1))
|
||||
|
|
@ -178,6 +189,11 @@ func _ready() -> void:
|
|||
parent_control.resized.connect(_refresh_panel_size)
|
||||
for page: SettingsBubblePage in _pages.values():
|
||||
page.hide_page()
|
||||
_controller_mapping_panel = ControllerMappingPanelType.new()
|
||||
add_child(_controller_mapping_panel)
|
||||
_controller_mapping_panel.closed.connect(
|
||||
_on_controller_mapping_panel_closed
|
||||
)
|
||||
_style_data_page()
|
||||
call_deferred("_refresh_panel_size")
|
||||
|
||||
|
|
@ -190,6 +206,13 @@ func setup_network_profile(
|
|||
_network_session = session
|
||||
|
||||
|
||||
func setup_controller_mapping(
|
||||
mapping_manager: ControllerMappingManagerType,
|
||||
) -> void:
|
||||
_controller_mapping_manager = mapping_manager
|
||||
_controller_mapping_panel.setup(_controller_mapping_manager)
|
||||
|
||||
|
||||
func setup_data_and_identity(
|
||||
data_root: PlayerDataRoot,
|
||||
identity_backups: IdentityBackupService,
|
||||
|
|
@ -263,6 +286,11 @@ func close_panel(immediate: bool = false) -> void:
|
|||
|
||||
func _finish_panel_close(applied_result: bool) -> void:
|
||||
_cancel_page_transition()
|
||||
if (
|
||||
_controller_mapping_panel != null
|
||||
and _controller_mapping_panel.is_open()
|
||||
):
|
||||
_controller_mapping_panel.close_panel()
|
||||
for page: SettingsBubblePage in _pages.values():
|
||||
page.hide_page()
|
||||
_page_stack.clear()
|
||||
|
|
@ -276,6 +304,12 @@ func _finish_panel_close(applied_result: bool) -> void:
|
|||
|
||||
|
||||
func handle_back() -> void:
|
||||
if (
|
||||
_controller_mapping_panel != null
|
||||
and _controller_mapping_panel.is_open()
|
||||
):
|
||||
_controller_mapping_panel.request_back()
|
||||
return
|
||||
if _page_transition_active:
|
||||
return
|
||||
if _page_stack.size() > 1:
|
||||
|
|
@ -284,10 +318,28 @@ func handle_back() -> void:
|
|||
close_panel()
|
||||
|
||||
|
||||
func _open_controller_mapping() -> void:
|
||||
if _controller_mapping_manager == null:
|
||||
_feedback.text = "no controller mapping service is available"
|
||||
return
|
||||
_controller_mapping_panel.open_panel()
|
||||
|
||||
|
||||
func _on_controller_mapping_panel_closed() -> void:
|
||||
%ControllerMapping.grab_focus()
|
||||
|
||||
|
||||
func get_active_page_id() -> StringName:
|
||||
return _page_stack.back() if not _page_stack.is_empty() else StringName()
|
||||
|
||||
|
||||
func is_controller_mapping_capturing() -> bool:
|
||||
return (
|
||||
_controller_mapping_panel != null
|
||||
and _controller_mapping_panel.is_capturing()
|
||||
)
|
||||
|
||||
|
||||
func _push_page(page_id: StringName) -> void:
|
||||
if (
|
||||
_page_transition_active
|
||||
|
|
@ -689,6 +741,9 @@ func _refresh_value_labels() -> void:
|
|||
+ _pixel_size_label(settings.ui_pixel_size)
|
||||
)
|
||||
_chat_dock.text = "chat dock\n" + ("right" if settings.chat_dock_right else "left")
|
||||
_chat_mode.text = (
|
||||
"chat mode\n" + ("mobile" if settings.chat_mobile_mode else "desktop")
|
||||
)
|
||||
_paint_dock.text = (
|
||||
"paint dock\n" + ("right" if settings.paint_dock_right else "left")
|
||||
)
|
||||
|
|
@ -774,6 +829,17 @@ func _toggle_chat_dock() -> void:
|
|||
_refresh_value_labels()
|
||||
|
||||
|
||||
func _toggle_chat_mode() -> void:
|
||||
if _settings_manager == null:
|
||||
return
|
||||
var edited: PlayerSettings = _settings_manager.current_settings.copy()
|
||||
edited.chat_mobile_mode = not edited.chat_mobile_mode
|
||||
if not _settings_manager.apply_settings(edited):
|
||||
_feedback.text = "failed to save chat mode setting."
|
||||
return
|
||||
_refresh_value_labels()
|
||||
|
||||
|
||||
func _toggle_paint_dock() -> void:
|
||||
if _settings_manager == null:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -157,8 +157,8 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
script = ExtResource("3_page")
|
||||
page_id = &"display"
|
||||
bubble_paths = Array[NodePath]([NodePath("BubbleCluster/WorldValue"), NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UIValue"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/ChatDock"), NodePath("BubbleCluster/PaintDock"), NodePath("BubbleCluster/DisplayBackButton")])
|
||||
focus_paths = Array[NodePath]([NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/ChatDock"), NodePath("BubbleCluster/PaintDock"), NodePath("BubbleCluster/DisplayBackButton")])
|
||||
bubble_paths = Array[NodePath]([NodePath("BubbleCluster/WorldValue"), NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UIValue"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/ChatDock"), NodePath("BubbleCluster/ChatMode"), NodePath("BubbleCluster/PaintDock"), NodePath("BubbleCluster/DisplayBackButton")])
|
||||
focus_paths = Array[NodePath]([NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/ChatDock"), NodePath("BubbleCluster/ChatMode"), NodePath("BubbleCluster/PaintDock"), NodePath("BubbleCluster/DisplayBackButton")])
|
||||
initial_focus_path = NodePath("BubbleCluster/WorldRetro")
|
||||
back_focus_path = NodePath("BubbleCluster/DisplayBackButton")
|
||||
compact_maximum_layout_size = Vector2(544, 400)
|
||||
|
|
@ -337,36 +337,48 @@ unique_name_in_owner = true
|
|||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "chat dock\nleft"
|
||||
neutral_size = Vector2(96, 96)
|
||||
desktop_anchor = Vector2(190, 435)
|
||||
compact_anchor = Vector2(160, 378)
|
||||
desktop_anchor = Vector2(105, 435)
|
||||
compact_anchor = Vector2(76, 378)
|
||||
compact_minimum_size = Vector2(82, 82)
|
||||
minimum_font_size = 12
|
||||
maximum_font_size = 16
|
||||
motion_phase = 1.1
|
||||
|
||||
[node name="ChatMode" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "chat mode\ndesktop"
|
||||
neutral_size = Vector2(96, 96)
|
||||
desktop_anchor = Vector2(275, 435)
|
||||
compact_anchor = Vector2(225, 378)
|
||||
compact_minimum_size = Vector2(82, 82)
|
||||
minimum_font_size = 12
|
||||
maximum_font_size = 16
|
||||
motion_phase = 1.4
|
||||
|
||||
[node name="PaintDock" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "paint dock\nright"
|
||||
neutral_size = Vector2(96, 96)
|
||||
desktop_anchor = Vector2(530, 435)
|
||||
compact_anchor = Vector2(448, 378)
|
||||
desktop_anchor = Vector2(445, 435)
|
||||
compact_anchor = Vector2(380, 378)
|
||||
compact_minimum_size = Vector2(82, 82)
|
||||
minimum_font_size = 12
|
||||
maximum_font_size = 16
|
||||
motion_phase = 1.65
|
||||
motion_phase = 1.7
|
||||
|
||||
[node name="DisplayBackButton" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "back"
|
||||
neutral_size = Vector2(96, 90)
|
||||
desktop_anchor = Vector2(350, 445)
|
||||
compact_anchor = Vector2(315, 390)
|
||||
desktop_anchor = Vector2(615, 435)
|
||||
compact_anchor = Vector2(530, 378)
|
||||
compact_minimum_size = Vector2(82, 78)
|
||||
minimum_font_size = 13
|
||||
maximum_font_size = 16
|
||||
motion_phase = 2.2
|
||||
motion_phase = 2.0
|
||||
|
||||
[node name="ControlsPage" type="Control" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -379,8 +391,8 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
script = ExtResource("3_page")
|
||||
page_id = &"controls"
|
||||
bubble_paths = Array[NodePath]([NodePath("BubbleCluster/MouseDecrease"), NodePath("BubbleCluster/MouseValue"), NodePath("BubbleCluster/MouseIncrease"), NodePath("BubbleCluster/ControllerDecrease"), NodePath("BubbleCluster/ControllerValue"), NodePath("BubbleCluster/ControllerIncrease"), NodePath("BubbleCluster/InvertYToggle"), NodePath("BubbleCluster/ControlsBackButton")])
|
||||
focus_paths = Array[NodePath]([NodePath("BubbleCluster/MouseDecrease"), NodePath("BubbleCluster/MouseValue"), NodePath("BubbleCluster/MouseIncrease"), NodePath("BubbleCluster/ControllerDecrease"), NodePath("BubbleCluster/ControllerValue"), NodePath("BubbleCluster/ControllerIncrease"), NodePath("BubbleCluster/InvertYToggle"), NodePath("BubbleCluster/ControlsBackButton")])
|
||||
bubble_paths = Array[NodePath]([NodePath("BubbleCluster/MouseDecrease"), NodePath("BubbleCluster/MouseValue"), NodePath("BubbleCluster/MouseIncrease"), NodePath("BubbleCluster/ControllerDecrease"), NodePath("BubbleCluster/ControllerValue"), NodePath("BubbleCluster/ControllerIncrease"), NodePath("BubbleCluster/InvertYToggle"), NodePath("BubbleCluster/ControllerMapping"), NodePath("BubbleCluster/ControlsBackButton")])
|
||||
focus_paths = Array[NodePath]([NodePath("BubbleCluster/MouseDecrease"), NodePath("BubbleCluster/MouseValue"), NodePath("BubbleCluster/MouseIncrease"), NodePath("BubbleCluster/ControllerDecrease"), NodePath("BubbleCluster/ControllerValue"), NodePath("BubbleCluster/ControllerIncrease"), NodePath("BubbleCluster/InvertYToggle"), NodePath("BubbleCluster/ControllerMapping"), NodePath("BubbleCluster/ControlsBackButton")])
|
||||
initial_focus_path = NodePath("BubbleCluster/MouseValue")
|
||||
back_focus_path = NodePath("BubbleCluster/ControlsBackButton")
|
||||
|
||||
|
|
@ -470,24 +482,36 @@ unique_name_in_owner = true
|
|||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "invert\nvertical\ncamera\noff"
|
||||
neutral_size = Vector2(176, 170)
|
||||
desktop_anchor = Vector2(525, 230)
|
||||
compact_anchor = Vector2(485, 225)
|
||||
compact_minimum_size = Vector2(170, 164)
|
||||
minimum_font_size = 14
|
||||
maximum_font_size = 24
|
||||
desktop_anchor = Vector2(525, 145)
|
||||
compact_anchor = Vector2(485, 135)
|
||||
motion_phase = 4.5
|
||||
|
||||
[node name="ControllerMapping" parent="ControlsPage/BubbleCluster" instance=ExtResource("4_bubble")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "map\ncontroller"
|
||||
neutral_size = Vector2(160, 150)
|
||||
desktop_anchor = Vector2(525, 330)
|
||||
compact_anchor = Vector2(485, 300)
|
||||
compact_minimum_size = Vector2(148, 138)
|
||||
minimum_font_size = 14
|
||||
maximum_font_size = 23
|
||||
motion_phase = 4.9
|
||||
|
||||
[node name="ControlsBackButton" parent="ControlsPage/BubbleCluster" instance=ExtResource("4_bubble")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "back"
|
||||
neutral_size = Vector2(100, 94)
|
||||
desktop_anchor = Vector2(525, 425)
|
||||
compact_anchor = Vector2(540, 395)
|
||||
desktop_anchor = Vector2(635, 445)
|
||||
compact_anchor = Vector2(555, 405)
|
||||
compact_minimum_size = Vector2(84, 80)
|
||||
minimum_font_size = 14
|
||||
maximum_font_size = 17
|
||||
motion_phase = 5.2
|
||||
motion_phase = 5.5
|
||||
|
||||
[node name="AccessibilityPage" type="Control" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
|
|
|||
|
|
@ -490,6 +490,11 @@ func _process(delta: float) -> void:
|
|||
func _input(event: InputEvent) -> void:
|
||||
if not visible:
|
||||
return
|
||||
if (
|
||||
_settings_panel.visible
|
||||
and _settings_panel.is_controller_mapping_capturing()
|
||||
):
|
||||
return
|
||||
if _join_game_page.visible:
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
_close_join_game()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue