Compare commits
2 commits
f2175ef6e3
...
8ca2c10f71
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ca2c10f71 | |||
| 3f48106e00 |
9 changed files with 230 additions and 52 deletions
107
main/main.gd
107
main/main.gd
|
|
@ -200,6 +200,8 @@ var _server_trust_dialog: ConfirmationDialog
|
||||||
var _pending_trust_changed: bool = false
|
var _pending_trust_changed: bool = false
|
||||||
var _identity_notice_dialog: AcceptDialog
|
var _identity_notice_dialog: AcceptDialog
|
||||||
var _data_setup_dialog: ConfirmationDialog
|
var _data_setup_dialog: ConfirmationDialog
|
||||||
|
var _data_setup_choose_button: Button
|
||||||
|
var _data_setup_current_button: Button
|
||||||
var _data_folder_dialog: FileDialog
|
var _data_folder_dialog: FileDialog
|
||||||
var _data_folder_picker_generation: int = 0
|
var _data_folder_picker_generation: int = 0
|
||||||
var _restore_data_setup_after_picker: bool = false
|
var _restore_data_setup_after_picker: bool = false
|
||||||
|
|
@ -657,10 +659,10 @@ func _show_data_root_setup() -> void:
|
||||||
_data_setup_dialog.cancel_button_text = "Quit"
|
_data_setup_dialog.cancel_button_text = "Quit"
|
||||||
_data_setup_dialog.confirmed.connect(_use_default_data_root)
|
_data_setup_dialog.confirmed.connect(_use_default_data_root)
|
||||||
_data_setup_dialog.canceled.connect(get_tree().quit)
|
_data_setup_dialog.canceled.connect(get_tree().quit)
|
||||||
_data_setup_dialog.add_button(
|
_data_setup_choose_button = _data_setup_dialog.add_button(
|
||||||
"Choose Another Folder", false, "choose"
|
"Choose Another Folder", false, "choose"
|
||||||
)
|
)
|
||||||
_data_setup_dialog.add_button(
|
_data_setup_current_button = _data_setup_dialog.add_button(
|
||||||
"Keep Current Location", false, "current"
|
"Keep Current Location", false, "current"
|
||||||
)
|
)
|
||||||
_data_setup_dialog.custom_action.connect(_on_data_setup_action)
|
_data_setup_dialog.custom_action.connect(_on_data_setup_action)
|
||||||
|
|
@ -675,20 +677,54 @@ func _show_data_root_setup() -> void:
|
||||||
% (default_path if not default_path.is_empty() else "Choose a folder")
|
% (default_path if not default_path.is_empty() else "Choose a folder")
|
||||||
)
|
)
|
||||||
_data_setup_dialog.ok_button_text = (
|
_data_setup_dialog.ok_button_text = (
|
||||||
"Move to Documents/NETFISHING" if legacy else "Use This Folder"
|
(
|
||||||
|
"Move to Documents/NETFISHING"
|
||||||
|
if legacy else "Use This Folder"
|
||||||
)
|
)
|
||||||
|
if not default_path.is_empty()
|
||||||
|
else "Keep Current Location"
|
||||||
|
)
|
||||||
|
_data_setup_current_button.visible = not default_path.is_empty()
|
||||||
if not _data_root.error_message.is_empty():
|
if not _data_root.error_message.is_empty():
|
||||||
_data_setup_dialog.dialog_text = (
|
_data_setup_dialog.dialog_text = (
|
||||||
"Your NETfishing data folder is unavailable.\n\n%s"
|
"Your NETfishing data folder is unavailable.\n\n%s"
|
||||||
% _data_root.error_message
|
% _data_root.error_message
|
||||||
)
|
)
|
||||||
_data_setup_dialog.popup_centered(Vector2i(640, 360))
|
_data_setup_dialog.popup_centered(Vector2i(640, 360))
|
||||||
|
_focus_data_setup_actions.call_deferred()
|
||||||
|
|
||||||
|
|
||||||
|
func _focus_data_setup_actions() -> void:
|
||||||
|
if _data_setup_dialog == null or not _data_setup_dialog.visible:
|
||||||
|
return
|
||||||
|
var actions: Array[Button] = [
|
||||||
|
_data_setup_dialog.get_ok_button(),
|
||||||
|
_data_setup_choose_button,
|
||||||
|
]
|
||||||
|
if _data_setup_current_button.visible:
|
||||||
|
actions.append(_data_setup_current_button)
|
||||||
|
actions.append(_data_setup_dialog.get_cancel_button())
|
||||||
|
for index: int in actions.size():
|
||||||
|
var action: Button = actions[index]
|
||||||
|
var previous: Button = actions[posmod(index - 1, actions.size())]
|
||||||
|
var next: Button = actions[(index + 1) % actions.size()]
|
||||||
|
action.focus_mode = Control.FOCUS_ALL
|
||||||
|
action.focus_neighbor_left = action.get_path_to(previous)
|
||||||
|
action.focus_neighbor_top = action.focus_neighbor_left
|
||||||
|
action.focus_neighbor_right = action.get_path_to(next)
|
||||||
|
action.focus_neighbor_bottom = action.focus_neighbor_right
|
||||||
|
_data_setup_dialog.get_ok_button().grab_focus()
|
||||||
|
|
||||||
|
|
||||||
func _use_default_data_root() -> void:
|
func _use_default_data_root() -> void:
|
||||||
var path: String = _data_root.default_visible_path()
|
var path: String = _data_root.default_visible_path()
|
||||||
if path.is_empty():
|
if path.is_empty():
|
||||||
_show_folder_picker()
|
_activate_selected_data_path(
|
||||||
|
ProjectSettings.globalize_path(
|
||||||
|
PlayerDataRoot.APP_DATA_PORTABLE_PATH
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
_activate_selected_data_path(path)
|
_activate_selected_data_path(path)
|
||||||
|
|
||||||
|
|
@ -737,7 +773,7 @@ func _open_folder_picker_after_modal(generation: int) -> void:
|
||||||
if not _data_root.default_visible_path().is_empty()
|
if not _data_root.default_visible_path().is_empty()
|
||||||
else OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
|
else OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
|
||||||
)
|
)
|
||||||
_data_folder_dialog.popup_centered_ratio(0.75)
|
_interface_fonts.popup_file_dialog(_data_folder_dialog)
|
||||||
|
|
||||||
|
|
||||||
func _on_data_folder_picker_canceled() -> void:
|
func _on_data_folder_picker_canceled() -> void:
|
||||||
|
|
@ -934,6 +970,9 @@ func _on_peer_identity_observed(_peer_id: int, status: String) -> void:
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
|
if _handle_data_root_controller_input(event):
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
if _game_ui.is_controller_mapping_capturing():
|
if _game_ui.is_controller_mapping_capturing():
|
||||||
return
|
return
|
||||||
if (
|
if (
|
||||||
|
|
@ -972,6 +1011,64 @@ func _input(event: InputEvent) -> void:
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_data_root_controller_input(event: InputEvent) -> bool:
|
||||||
|
var button_event := event as InputEventJoypadButton
|
||||||
|
if button_event == null or not button_event.pressed:
|
||||||
|
return false
|
||||||
|
var setup_visible: bool = (
|
||||||
|
_data_setup_dialog != null and _data_setup_dialog.visible
|
||||||
|
)
|
||||||
|
var picker_visible: bool = (
|
||||||
|
_data_folder_dialog != null and _data_folder_dialog.visible
|
||||||
|
)
|
||||||
|
if not setup_visible and not picker_visible:
|
||||||
|
return false
|
||||||
|
var use_mapping: bool = _controller_mapping_manager.has_custom_mapping()
|
||||||
|
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
|
||||||
|
or event.is_action_pressed("ui_accept")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var cancel_pressed: bool = (
|
||||||
|
_controller_mapping_manager.event_matches_role(
|
||||||
|
event,
|
||||||
|
ControllerMappingManagerType.ROLE_B,
|
||||||
|
)
|
||||||
|
if use_mapping
|
||||||
|
else (
|
||||||
|
button_event.button_index == JOY_BUTTON_B
|
||||||
|
or event.is_action_pressed("ui_cancel")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if cancel_pressed:
|
||||||
|
if picker_visible:
|
||||||
|
_on_data_folder_picker_canceled()
|
||||||
|
else:
|
||||||
|
_data_setup_dialog.get_cancel_button().pressed.emit()
|
||||||
|
return true
|
||||||
|
if not accept_pressed:
|
||||||
|
return false
|
||||||
|
var focused: Control = (
|
||||||
|
_data_folder_dialog.gui_get_focus_owner()
|
||||||
|
if picker_visible
|
||||||
|
else _data_setup_dialog.gui_get_focus_owner()
|
||||||
|
)
|
||||||
|
var focused_button := focused as BaseButton
|
||||||
|
if focused_button != null and not focused_button.disabled:
|
||||||
|
focused_button.pressed.emit()
|
||||||
|
return true
|
||||||
|
if setup_visible:
|
||||||
|
_data_setup_dialog.get_ok_button().pressed.emit()
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
func _is_pause_open_request(event: InputEvent) -> bool:
|
func _is_pause_open_request(event: InputEvent) -> bool:
|
||||||
return (
|
return (
|
||||||
event.is_action_pressed("open_system_menu")
|
event.is_action_pressed("open_system_menu")
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ extends RefCounted
|
||||||
|
|
||||||
const CAPABILITY: StringName = &"chat_v1"
|
const CAPABILITY: StringName = &"chat_v1"
|
||||||
const RELIABLE_CHANNEL: int = NetworkProtocol.CHAT_RELIABLE_CHANNEL
|
const RELIABLE_CHANNEL: int = NetworkProtocol.CHAT_RELIABLE_CHANNEL
|
||||||
const MAX_VISIBLE_CHARACTERS: int = 300
|
const MAX_VISIBLE_CHARACTERS: int = 256
|
||||||
const MAX_UTF8_BYTES: int = 1200
|
const MAX_UTF8_BYTES: int = 1024
|
||||||
const MAX_HISTORY: int = 100
|
const MAX_HISTORY: int = 100
|
||||||
const LATE_JOIN_HISTORY: int = 50
|
const LATE_JOIN_HISTORY: int = 50
|
||||||
const MAX_ID_LENGTH: int = 96
|
const MAX_ID_LENGTH: int = 96
|
||||||
|
|
|
||||||
|
|
@ -1446,6 +1446,10 @@ func _is_valid_movement_input(data: Dictionary) -> bool:
|
||||||
or typeof(data.get("sprint")) != TYPE_BOOL
|
or typeof(data.get("sprint")) != TYPE_BOOL
|
||||||
or typeof(data.get("sneak")) != TYPE_BOOL
|
or typeof(data.get("sneak")) != TYPE_BOOL
|
||||||
or typeof(data.get("slow_walk")) != TYPE_BOOL
|
or typeof(data.get("slow_walk")) != TYPE_BOOL
|
||||||
|
or (
|
||||||
|
data.has("sitting")
|
||||||
|
and typeof(data.get("sitting")) != TYPE_BOOL
|
||||||
|
)
|
||||||
or typeof(data.get("camera_yaw")) not in [TYPE_FLOAT, TYPE_INT]
|
or typeof(data.get("camera_yaw")) not in [TYPE_FLOAT, TYPE_INT]
|
||||||
):
|
):
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,17 @@ func resolve() -> bool:
|
||||||
|
|
||||||
func default_visible_path() -> String:
|
func default_visible_path() -> String:
|
||||||
var documents: String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
|
var documents: String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
|
||||||
return documents.path_join("NETFISHING") if not documents.is_empty() else ""
|
if documents.is_empty():
|
||||||
|
return ""
|
||||||
|
var candidate: String = _normalize(documents.path_join("NETFISHING"))
|
||||||
|
if not candidate.is_absolute_path():
|
||||||
|
return ""
|
||||||
|
var classification: StringName = classify_candidate_path(
|
||||||
|
candidate,
|
||||||
|
ProjectSettings.globalize_path("res://"),
|
||||||
|
OS.get_executable_path().get_base_dir(),
|
||||||
|
)
|
||||||
|
return candidate if classification == PATH_ALLOWED else ""
|
||||||
|
|
||||||
|
|
||||||
func select_new_root(path: String, app_data: bool = false) -> bool:
|
func select_new_root(path: String, app_data: bool = false) -> bool:
|
||||||
|
|
@ -232,14 +242,21 @@ func _validate_candidate(path: String, create: bool) -> bool:
|
||||||
var normalized: String = _normalize(path)
|
var normalized: String = _normalize(path)
|
||||||
if normalized.is_empty() or not normalized.is_absolute_path():
|
if normalized.is_empty() or not normalized.is_absolute_path():
|
||||||
return _fail("Choose an absolute filesystem folder.")
|
return _fail("Choose an absolute filesystem folder.")
|
||||||
|
var app_data_path: String = _normalize(
|
||||||
|
ProjectSettings.globalize_path(APP_DATA_PORTABLE_PATH)
|
||||||
|
)
|
||||||
|
var exported_app_data: bool = (
|
||||||
|
normalized == app_data_path
|
||||||
|
and not OS.has_feature("editor")
|
||||||
|
)
|
||||||
var classification: StringName = classify_candidate_path(
|
var classification: StringName = classify_candidate_path(
|
||||||
normalized,
|
normalized,
|
||||||
ProjectSettings.globalize_path("res://"),
|
ProjectSettings.globalize_path("res://"),
|
||||||
OS.get_executable_path().get_base_dir(),
|
OS.get_executable_path().get_base_dir(),
|
||||||
)
|
)
|
||||||
if classification == PATH_SOURCE_PROJECT:
|
if classification == PATH_SOURCE_PROJECT and not exported_app_data:
|
||||||
return _fail("The project folder cannot be used as the player data folder.")
|
return _fail("The project folder cannot be used as the player data folder.")
|
||||||
if classification == PATH_INSTALLATION:
|
if classification == PATH_INSTALLATION and not exported_app_data:
|
||||||
return _fail(
|
return _fail(
|
||||||
"The application installation folder cannot be used as the player data folder."
|
"The application installation folder cannot be used as the player data folder."
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -533,6 +533,7 @@ func capture_network_input(sequence: int) -> Dictionary:
|
||||||
"sprint": false,
|
"sprint": false,
|
||||||
"sneak": false,
|
"sneak": false,
|
||||||
"slow_walk": false,
|
"slow_walk": false,
|
||||||
|
"sitting": _sitting,
|
||||||
}
|
}
|
||||||
var axis: Vector2 = Input.get_vector(
|
var axis: Vector2 = Input.get_vector(
|
||||||
"move_left",
|
"move_left",
|
||||||
|
|
@ -548,6 +549,7 @@ func capture_network_input(sequence: int) -> Dictionary:
|
||||||
"sprint": Input.is_action_pressed("sprint"),
|
"sprint": Input.is_action_pressed("sprint"),
|
||||||
"sneak": Input.is_action_pressed("sneak"),
|
"sneak": Input.is_action_pressed("sneak"),
|
||||||
"slow_walk": Input.is_action_pressed("slow_walk"),
|
"slow_walk": Input.is_action_pressed("slow_walk"),
|
||||||
|
"sitting": _sitting,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -565,6 +567,7 @@ func apply_authoritative_network_input(data: Dictionary) -> void:
|
||||||
_network_sprint = bool(data.get("sprint", false))
|
_network_sprint = bool(data.get("sprint", false))
|
||||||
_network_sneak = bool(data.get("sneak", false))
|
_network_sneak = bool(data.get("sneak", false))
|
||||||
_network_slow_walk = bool(data.get("slow_walk", false))
|
_network_slow_walk = bool(data.get("slow_walk", false))
|
||||||
|
_set_sitting(bool(data.get("sitting", false)))
|
||||||
|
|
||||||
|
|
||||||
func make_network_snapshot(peer_id: int) -> Dictionary:
|
func make_network_snapshot(peer_id: int) -> Dictionary:
|
||||||
|
|
@ -575,6 +578,7 @@ func make_network_snapshot(peer_id: int) -> Dictionary:
|
||||||
"velocity": [velocity.x, velocity.y, velocity.z],
|
"velocity": [velocity.x, velocity.y, velocity.z],
|
||||||
"visual_yaw": _visuals.rotation.y,
|
"visual_yaw": _visuals.rotation.y,
|
||||||
"grounded": is_on_floor(),
|
"grounded": is_on_floor(),
|
||||||
|
"sitting": _sitting,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -585,6 +589,7 @@ func push_network_snapshot(snapshot: Dictionary) -> void:
|
||||||
_network_target_position = parsed["position"]
|
_network_target_position = parsed["position"]
|
||||||
_network_target_velocity = parsed["velocity"]
|
_network_target_velocity = parsed["velocity"]
|
||||||
_network_target_visual_yaw = parsed["visual_yaw"]
|
_network_target_visual_yaw = parsed["visual_yaw"]
|
||||||
|
_set_sitting(bool(parsed["sitting"]))
|
||||||
if not _network_snapshot_ready:
|
if not _network_snapshot_ready:
|
||||||
global_position = _network_target_position
|
global_position = _network_target_position
|
||||||
velocity = _network_target_velocity
|
velocity = _network_target_velocity
|
||||||
|
|
@ -596,6 +601,7 @@ func apply_local_prediction_correction(snapshot: Dictionary) -> void:
|
||||||
var parsed: Dictionary = _parse_network_snapshot(snapshot)
|
var parsed: Dictionary = _parse_network_snapshot(snapshot)
|
||||||
if parsed.is_empty():
|
if parsed.is_empty():
|
||||||
return
|
return
|
||||||
|
_set_sitting(bool(parsed["sitting"]))
|
||||||
var authoritative_position: Vector3 = parsed["position"]
|
var authoritative_position: Vector3 = parsed["position"]
|
||||||
var error_distance: float = global_position.distance_to(
|
var error_distance: float = global_position.distance_to(
|
||||||
authoritative_position
|
authoritative_position
|
||||||
|
|
@ -613,6 +619,7 @@ func apply_network_teleport(snapshot: Dictionary) -> void:
|
||||||
global_position = parsed["position"]
|
global_position = parsed["position"]
|
||||||
velocity = parsed["velocity"]
|
velocity = parsed["velocity"]
|
||||||
_visuals.rotation.y = parsed["visual_yaw"]
|
_visuals.rotation.y = parsed["visual_yaw"]
|
||||||
|
_set_sitting(bool(parsed["sitting"]))
|
||||||
_network_target_position = global_position
|
_network_target_position = global_position
|
||||||
_network_target_velocity = velocity
|
_network_target_velocity = velocity
|
||||||
_network_target_visual_yaw = _visuals.rotation.y
|
_network_target_visual_yaw = _visuals.rotation.y
|
||||||
|
|
@ -640,6 +647,9 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary:
|
||||||
float(network_velocity[2])
|
float(network_velocity[2])
|
||||||
)
|
)
|
||||||
var visual_yaw: float = float(snapshot.get("visual_yaw", 0.0))
|
var visual_yaw: float = float(snapshot.get("visual_yaw", 0.0))
|
||||||
|
if snapshot.has("sitting") and typeof(snapshot.get("sitting")) != TYPE_BOOL:
|
||||||
|
return {}
|
||||||
|
var sitting: bool = bool(snapshot.get("sitting", false))
|
||||||
if (
|
if (
|
||||||
not parsed_position.is_finite()
|
not parsed_position.is_finite()
|
||||||
or not parsed_velocity.is_finite()
|
or not parsed_velocity.is_finite()
|
||||||
|
|
@ -650,6 +660,7 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary:
|
||||||
"position": parsed_position,
|
"position": parsed_position,
|
||||||
"velocity": parsed_velocity,
|
"velocity": parsed_velocity,
|
||||||
"visual_yaw": visual_yaw,
|
"visual_yaw": visual_yaw,
|
||||||
|
"sitting": sitting,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const DEFAULT_WORLD_PIXEL_SIZE: int = 3
|
||||||
const MIN_UI_PIXEL_SIZE: int = 1
|
const MIN_UI_PIXEL_SIZE: int = 1
|
||||||
const MAX_UI_PIXEL_SIZE: int = 5
|
const MAX_UI_PIXEL_SIZE: int = 5
|
||||||
const DEFAULT_UI_PIXEL_SIZE: int = 3
|
const DEFAULT_UI_PIXEL_SIZE: int = 3
|
||||||
const MAX_CHAT_DRAFT_CHARACTERS: int = 280
|
const MAX_CHAT_DRAFT_CHARACTERS: int = 256
|
||||||
const COMPACT_DISPLAY_HEIGHT: int = 560
|
const COMPACT_DISPLAY_HEIGHT: int = 560
|
||||||
const WORLD_DESKTOP_GRID_HEIGHTS: Array[int] = [0, 540, 360, 216, 96]
|
const WORLD_DESKTOP_GRID_HEIGHTS: Array[int] = [0, 540, 360, 216, 96]
|
||||||
const WORLD_COMPACT_GRID_HEIGHTS: Array[int] = [0, 360, 240, 144, 72]
|
const WORLD_COMPACT_GRID_HEIGHTS: Array[int] = [0, 360, 240, 144, 72]
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ const HANDLE_SIZE := Vector2(34, 42)
|
||||||
const HANDLE_GAP: float = 6.0
|
const HANDLE_GAP: float = 6.0
|
||||||
const COLLAPSED_REVEAL_WIDTH: float = 8.0
|
const COLLAPSED_REVEAL_WIDTH: float = 8.0
|
||||||
const HINT_EDGE_MARGIN: float = 4.0
|
const HINT_EDGE_MARGIN: float = 4.0
|
||||||
|
const SPEECH_BUBBLE_WIDTH: float = 320.0
|
||||||
const MOBILE_COMPACT_WIDTH: float = 620.0
|
const MOBILE_COMPACT_WIDTH: float = 620.0
|
||||||
const MOBILE_EXPANDED_WIDTH: float = 820.0
|
const MOBILE_EXPANDED_WIDTH: float = 820.0
|
||||||
const MOBILE_COMPACT_HEIGHT: float = 220.0
|
const MOBILE_COMPACT_HEIGHT: float = 220.0
|
||||||
|
|
@ -78,6 +79,7 @@ var _pending_send_body: String = ""
|
||||||
var _prior_movement: bool = true
|
var _prior_movement: bool = true
|
||||||
var _prior_camera: bool = true
|
var _prior_camera: bool = true
|
||||||
var _controller_refocused: bool = false
|
var _controller_refocused: bool = false
|
||||||
|
var _input_lock_applied: bool = false
|
||||||
var _output_scale: float = 1.0
|
var _output_scale: float = 1.0
|
||||||
var _dock_right: bool = false
|
var _dock_right: bool = false
|
||||||
var _mobile_mode: bool = false
|
var _mobile_mode: bool = false
|
||||||
|
|
@ -164,6 +166,7 @@ func open_chat() -> void:
|
||||||
text_entry_ownership_changed.emit(true)
|
text_entry_ownership_changed.emit(true)
|
||||||
_prior_movement = _player.is_movement_enabled()
|
_prior_movement = _player.is_movement_enabled()
|
||||||
_prior_camera = _player.is_camera_input_enabled()
|
_prior_camera = _player.is_camera_input_enabled()
|
||||||
|
_input_lock_applied = true
|
||||||
_player.set_movement_enabled(false)
|
_player.set_movement_enabled(false)
|
||||||
_player.set_camera_input_enabled(false)
|
_player.set_camera_input_enabled(false)
|
||||||
_fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true)
|
_fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true)
|
||||||
|
|
@ -191,16 +194,18 @@ func set_available(value: bool) -> void:
|
||||||
|
|
||||||
|
|
||||||
func close_chat() -> void:
|
func close_chat() -> void:
|
||||||
if not _opened:
|
var ownership_was_active: bool = _opened or _input_lock_applied
|
||||||
return
|
|
||||||
_opened = false
|
_opened = false
|
||||||
text_entry_ownership_changed.emit(false)
|
|
||||||
_entry.virtual_keyboard_enabled = false
|
_entry.virtual_keyboard_enabled = false
|
||||||
_entry.release_focus()
|
_entry.release_focus()
|
||||||
_entry.hide()
|
_entry.hide()
|
||||||
|
if _input_lock_applied:
|
||||||
_player.set_movement_enabled(_prior_movement)
|
_player.set_movement_enabled(_prior_movement)
|
||||||
_player.set_camera_input_enabled(_prior_camera)
|
_player.set_camera_input_enabled(_prior_camera)
|
||||||
_fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false)
|
_fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false)
|
||||||
|
_input_lock_applied = false
|
||||||
|
if ownership_was_active:
|
||||||
|
text_entry_ownership_changed.emit(false)
|
||||||
_flush_draft()
|
_flush_draft()
|
||||||
_refresh_visibility()
|
_refresh_visibility()
|
||||||
_refresh_input_ownership()
|
_refresh_input_ownership()
|
||||||
|
|
@ -338,7 +343,7 @@ func _build_ui() -> void:
|
||||||
_history.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
_history.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
_history.fit_content = false
|
_history.fit_content = false
|
||||||
_history.scroll_active = true
|
_history.scroll_active = true
|
||||||
_history.scroll_following = false
|
_history.scroll_following = true
|
||||||
_history.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM
|
_history.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM
|
||||||
_history.mouse_filter = Control.MOUSE_FILTER_STOP
|
_history.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
_history.add_theme_font_override("normal_font", UtilityPageStyle.TuffyFont)
|
_history.add_theme_font_override("normal_font", UtilityPageStyle.TuffyFont)
|
||||||
|
|
@ -623,13 +628,14 @@ func _on_message(message: Dictionary) -> void:
|
||||||
_on_peer_removed(peer_id)
|
_on_peer_removed(peer_id)
|
||||||
var bubble := PanelContainer.new()
|
var bubble := PanelContainer.new()
|
||||||
bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
bubble.custom_minimum_size = Vector2(270, 0)
|
bubble.custom_minimum_size = Vector2(SPEECH_BUBBLE_WIDTH, 0.0)
|
||||||
bubble.size = Vector2(270, 72)
|
|
||||||
bubble.add_theme_stylebox_override("panel", _speech_panel_style())
|
bubble.add_theme_stylebox_override("panel", _speech_panel_style())
|
||||||
var label := Label.new()
|
var label := Label.new()
|
||||||
label.text = str(message["body"]).left(120)
|
label.text = str(message["body"])
|
||||||
label.custom_minimum_size = Vector2(246, 46)
|
label.custom_minimum_size = Vector2(
|
||||||
label.size = Vector2(246, 62)
|
SPEECH_BUBBLE_WIDTH - 20.0,
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
|
|
@ -670,7 +676,6 @@ func _on_rejected(message: String) -> void:
|
||||||
func _refresh_history() -> void:
|
func _refresh_history() -> void:
|
||||||
if _service == null:
|
if _service == null:
|
||||||
return
|
return
|
||||||
var scroll_state := _capture_history_scroll()
|
|
||||||
var lines: Array[String] = []
|
var lines: Array[String] = []
|
||||||
var messages := _service.get_history()
|
var messages := _service.get_history()
|
||||||
for message: Dictionary in messages:
|
for message: Dictionary in messages:
|
||||||
|
|
@ -681,7 +686,7 @@ func _refresh_history() -> void:
|
||||||
str(message["sender_display_name"]), str(message["body"]),
|
str(message["sender_display_name"]), str(message["body"]),
|
||||||
])
|
])
|
||||||
_history.text = "\n".join(lines)
|
_history.text = "\n".join(lines)
|
||||||
_restore_history_scroll.call_deferred(scroll_state)
|
_scroll_history_to_bottom.call_deferred()
|
||||||
|
|
||||||
|
|
||||||
func _refresh_visibility() -> void:
|
func _refresh_visibility() -> void:
|
||||||
|
|
@ -1034,7 +1039,6 @@ func _layout_presentation(animate: bool) -> void:
|
||||||
_unread_indicator.position = unread_position
|
_unread_indicator.position = unread_position
|
||||||
_hint.position = hint_position
|
_hint.position = hint_position
|
||||||
return
|
return
|
||||||
var scroll_state := _capture_history_scroll()
|
|
||||||
_height_tween = create_tween().set_parallel(true)
|
_height_tween = create_tween().set_parallel(true)
|
||||||
_height_tween.tween_property(
|
_height_tween.tween_property(
|
||||||
_panel, "position", target_position, UIMotion.CHAT_RESIZE_DURATION
|
_panel, "position", target_position, UIMotion.CHAT_RESIZE_DURATION
|
||||||
|
|
@ -1073,38 +1077,18 @@ func _layout_presentation(animate: bool) -> void:
|
||||||
UIMotion.CHAT_RESIZE_DURATION,
|
UIMotion.CHAT_RESIZE_DURATION,
|
||||||
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||||
_height_tween.finished.connect(func() -> void:
|
_height_tween.finished.connect(func() -> void:
|
||||||
_restore_history_scroll(scroll_state)
|
_scroll_history_to_bottom()
|
||||||
)
|
)
|
||||||
_hint.position = hint_position
|
_hint.position = hint_position
|
||||||
|
|
||||||
|
|
||||||
func _capture_history_scroll() -> Dictionary:
|
func _scroll_history_to_bottom() -> void:
|
||||||
if _history == null:
|
|
||||||
return {"pinned": true, "value": 0.0}
|
|
||||||
var scroll := _history.get_v_scroll_bar()
|
|
||||||
if scroll == null:
|
|
||||||
return {"pinned": true, "value": 0.0}
|
|
||||||
var bottom := maxf(scroll.min_value, scroll.max_value - scroll.page)
|
|
||||||
return {
|
|
||||||
"pinned": scroll.value >= bottom - 2.0,
|
|
||||||
"value": scroll.value,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func _restore_history_scroll(state: Dictionary) -> void:
|
|
||||||
if _history == null:
|
if _history == null:
|
||||||
return
|
return
|
||||||
var scroll := _history.get_v_scroll_bar()
|
var scroll := _history.get_v_scroll_bar()
|
||||||
if scroll == null:
|
if scroll == null:
|
||||||
return
|
return
|
||||||
if bool(state.get("pinned", true)):
|
|
||||||
scroll.value = maxf(scroll.min_value, scroll.max_value - scroll.page)
|
scroll.value = maxf(scroll.min_value, scroll.max_value - scroll.page)
|
||||||
else:
|
|
||||||
scroll.value = clampf(
|
|
||||||
float(state.get("value", 0.0)),
|
|
||||||
scroll.min_value,
|
|
||||||
maxf(scroll.min_value, scroll.max_value - scroll.page),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_viewport_resized() -> void:
|
func _on_viewport_resized() -> void:
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,13 @@ class_name InterfaceFontController
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
const STANDARD_FONT: Font = preload("res://ui/fonts/Tuffy_Bold.otf")
|
const STANDARD_FONT: Font = preload("res://ui/fonts/Tuffy_Bold.otf")
|
||||||
|
const COMPACT_FILE_DIALOG_LIMIT := Vector2i(800, 600)
|
||||||
|
const COMPACT_FILE_DIALOG_MARGIN := Vector2i(12, 12)
|
||||||
|
const COMPACT_FILE_DIALOG_FONT_SIZE: int = 17
|
||||||
|
|
||||||
var _game_theme: Theme = preload("res://ui/game_theme.tres")
|
var _game_theme: Theme = preload("res://ui/game_theme.tres")
|
||||||
var _utility_theme: Theme
|
var _utility_theme: Theme
|
||||||
|
var _compact_file_dialog_theme: Theme
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
@ -40,5 +44,66 @@ func apply_utility_theme(themed_node: Node) -> void:
|
||||||
(themed_node as Window).theme = _utility_theme
|
(themed_node as Window).theme = _utility_theme
|
||||||
|
|
||||||
|
|
||||||
|
func popup_file_dialog(dialog: FileDialog) -> void:
|
||||||
|
if dialog == null or not dialog.is_inside_tree():
|
||||||
|
return
|
||||||
|
var host_window: Window = dialog.get_parent().get_window()
|
||||||
|
var host_size: Vector2i = host_window.size
|
||||||
|
var compact: bool = (
|
||||||
|
host_size.x <= COMPACT_FILE_DIALOG_LIMIT.x
|
||||||
|
or host_size.y <= COMPACT_FILE_DIALOG_LIMIT.y
|
||||||
|
)
|
||||||
|
if not compact:
|
||||||
|
apply_utility_theme(dialog)
|
||||||
|
dialog.min_size = Vector2i(200, 70)
|
||||||
|
dialog.max_size = Vector2i.ZERO
|
||||||
|
dialog.popup_centered_ratio(0.75)
|
||||||
|
return
|
||||||
|
if _compact_file_dialog_theme == null:
|
||||||
|
_compact_file_dialog_theme = _utility_theme.duplicate(true)
|
||||||
|
_compact_file_dialog_theme.default_font_size = (
|
||||||
|
COMPACT_FILE_DIALOG_FONT_SIZE
|
||||||
|
)
|
||||||
|
dialog.theme = _compact_file_dialog_theme
|
||||||
|
var available_size := Vector2i(
|
||||||
|
maxi(1, host_size.x - COMPACT_FILE_DIALOG_MARGIN.x * 2),
|
||||||
|
maxi(1, host_size.y - COMPACT_FILE_DIALOG_MARGIN.y * 2),
|
||||||
|
)
|
||||||
|
dialog.min_size = Vector2i.ZERO
|
||||||
|
dialog.max_size = available_size
|
||||||
|
dialog.popup_centered(available_size)
|
||||||
|
# FileDialog's desktop-oriented intrinsic minimum can exceed a compact
|
||||||
|
# 4:3 display. The compact font keeps its controls usable while this final
|
||||||
|
# clamp guarantees the embedded window cannot escape the physical screen.
|
||||||
|
dialog.size = available_size
|
||||||
|
if dialog.get_viewport().gui_embed_subwindows:
|
||||||
|
dialog.position = (host_size - available_size) / 2
|
||||||
|
_finalize_compact_file_dialog.call_deferred(
|
||||||
|
dialog, host_size, available_size
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _finalize_compact_file_dialog(
|
||||||
|
dialog: FileDialog,
|
||||||
|
host_size: Vector2i,
|
||||||
|
requested_size: Vector2i,
|
||||||
|
) -> void:
|
||||||
|
await get_tree().process_frame
|
||||||
|
if dialog == null or not dialog.visible:
|
||||||
|
return
|
||||||
|
# Embedded windows report their content origin below the title bar. Account
|
||||||
|
# for that inset after Godot has laid the window out so its bottom and right
|
||||||
|
# edges retain the same safe margin as its decorated top and left edges.
|
||||||
|
var safe_bottom_right: Vector2i = host_size - COMPACT_FILE_DIALOG_MARGIN
|
||||||
|
var fitted_size: Vector2i = requested_size
|
||||||
|
fitted_size.x -= maxi(
|
||||||
|
0, dialog.position.x + fitted_size.x - safe_bottom_right.x
|
||||||
|
)
|
||||||
|
fitted_size.y -= maxi(
|
||||||
|
0, dialog.position.y + fitted_size.y - safe_bottom_right.y
|
||||||
|
)
|
||||||
|
dialog.size = Vector2i(maxi(1, fitted_size.x), maxi(1, fitted_size.y))
|
||||||
|
|
||||||
|
|
||||||
func readable_font() -> Font:
|
func readable_font() -> Font:
|
||||||
return STANDARD_FONT
|
return STANDARD_FONT
|
||||||
|
|
|
||||||
|
|
@ -472,7 +472,7 @@ func _choose_data_folder() -> void:
|
||||||
_interface_fonts.apply_utility_theme(_data_folder_dialog)
|
_interface_fonts.apply_utility_theme(_data_folder_dialog)
|
||||||
add_child(_data_folder_dialog)
|
add_child(_data_folder_dialog)
|
||||||
_data_folder_dialog.current_dir = _data_root.root_path.get_base_dir()
|
_data_folder_dialog.current_dir = _data_root.root_path.get_base_dir()
|
||||||
_data_folder_dialog.popup_centered_ratio(0.75)
|
_interface_fonts.popup_file_dialog(_data_folder_dialog)
|
||||||
|
|
||||||
|
|
||||||
func _change_data_folder(path: String) -> void:
|
func _change_data_folder(path: String) -> void:
|
||||||
|
|
@ -540,7 +540,7 @@ func _choose_identity_export(identity_type: String) -> void:
|
||||||
add_child(_export_file_dialog)
|
add_child(_export_file_dialog)
|
||||||
_export_file_dialog.current_dir = suggested.get_base_dir()
|
_export_file_dialog.current_dir = suggested.get_base_dir()
|
||||||
_export_file_dialog.current_file = suggested.get_file()
|
_export_file_dialog.current_file = suggested.get_file()
|
||||||
_export_file_dialog.popup_centered_ratio(0.75)
|
_interface_fonts.popup_file_dialog(_export_file_dialog)
|
||||||
|
|
||||||
|
|
||||||
func _identity_export_file_selected(path: String) -> void:
|
func _identity_export_file_selected(path: String) -> void:
|
||||||
|
|
@ -565,7 +565,7 @@ func _choose_identity_import(identity_type: String) -> void:
|
||||||
_interface_fonts.apply_utility_theme(_backup_file_dialog)
|
_interface_fonts.apply_utility_theme(_backup_file_dialog)
|
||||||
add_child(_backup_file_dialog)
|
add_child(_backup_file_dialog)
|
||||||
_backup_file_dialog.current_dir = _data_root.identity_backup_directory()
|
_backup_file_dialog.current_dir = _data_root.identity_backup_directory()
|
||||||
_backup_file_dialog.popup_centered_ratio(0.75)
|
_interface_fonts.popup_file_dialog(_backup_file_dialog)
|
||||||
|
|
||||||
|
|
||||||
func _identity_import_file_selected(path: String) -> void:
|
func _identity_import_file_selected(path: String) -> void:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue