diff --git a/fishing/fishing_presentation.gd b/fishing/fishing_presentation.gd index a44ed7f..62501e2 100644 --- a/fishing/fishing_presentation.gd +++ b/fishing/fishing_presentation.gd @@ -216,12 +216,12 @@ func show_bite() -> void: _bite_tween.finished.connect(_on_bite_tween_finished) -func show_withdrawal_position(position: Vector3) -> void: +func show_withdrawal_position(world_position: Vector3) -> void: if _mode != VisualMode.FISHING: return _kill_active_tween() - _bobber.global_position = _with_water_height(position) + _bobber.global_position = _with_water_height(world_position) func begin_reeling() -> void: @@ -232,12 +232,12 @@ func begin_reeling() -> void: _cast_position = _with_water_height(_bobber.global_position) -func show_reel_position(position: Vector3, _input_held: bool) -> void: +func show_reel_position(world_position: Vector3, _input_held: bool) -> void: if _mode != VisualMode.FISHING: return _kill_active_tween() - _bobber.global_position = _with_water_height(position) + _bobber.global_position = _with_water_height(world_position) func play_outcome(outcome: StringName) -> void: @@ -371,8 +371,12 @@ func _calculate_pickup_position(target: Vector3) -> Vector3: return player_on_water + outward * pickup_distance -func _with_water_height(position: Vector3) -> Vector3: - return Vector3(position.x, get_water_surface_height(), position.z) +func _with_water_height(world_position: Vector3) -> Vector3: + return Vector3( + world_position.x, + get_water_surface_height(), + world_position.z, + ) func _update_line(delta: float) -> void: diff --git a/fishing/fishing_spot.gd b/fishing/fishing_spot.gd index fd2e01c..1655c6d 100644 --- a/fishing/fishing_spot.gd +++ b/fishing/fishing_spot.gd @@ -1082,7 +1082,7 @@ func get_fishable_water_region( query, 32 ) - var selected_region: FishableWaterRegionType + var selected_region: FishableWaterRegionType = null for result: Dictionary in results: var collider: Object = result.get("collider") var region: FishableWaterRegionType = collider as FishableWaterRegionType diff --git a/fishing/remote_fishing_presentation.gd b/fishing/remote_fishing_presentation.gd index 98e5ea8..3d20ee9 100644 --- a/fishing/remote_fishing_presentation.gd +++ b/fishing/remote_fishing_presentation.gd @@ -9,8 +9,8 @@ var _target: Vector3 var _active: bool = false -func setup(owner: Player) -> void: - _owner = owner +func setup(owning_player: Player) -> void: + _owner = owning_player _bobber = MeshInstance3D.new() var bobber_mesh := SphereMesh.new() bobber_mesh.radius = 0.12 @@ -43,11 +43,11 @@ func show_cast(target: Vector3) -> void: _redraw_line() -func update_bobber(position: Vector3) -> void: - if not _active or not position.is_finite(): +func update_bobber(world_position: Vector3) -> void: + if not _active or not world_position.is_finite(): return - _target = position - _bobber.global_position = position + _target = world_position + _bobber.global_position = world_position _redraw_line() diff --git a/main/main.gd b/main/main.gd index 590aca9..d8e86e8 100644 --- a/main/main.gd +++ b/main/main.gd @@ -88,6 +88,7 @@ const TITLE_MUSIC_SILENCE_DB: float = -80.0 @onready var _water_recovery: WaterRecoveryControllerType = %WaterRecovery @onready var _save_manager: PlayerSaveManagerType = %PlayerSaveManager @onready var _settings_manager: PlayerSettingsManagerType = %PlayerSettingsManager +@onready var _interface_fonts: InterfaceFontController = %InterfaceFontController @onready var _title_music: AudioStreamPlayer = %TitleMusic @onready var _ui_pixelation: UIPixelationPresenterType = %UIPresentation @onready var _pixelation_reset: PixelationResetOverlayType = ( @@ -146,11 +147,21 @@ var _pending_trust_changed: bool = false var _identity_notice_dialog: AcceptDialog var _data_setup_dialog: ConfirmationDialog var _data_folder_dialog: FileDialog +var _data_folder_picker_generation: int = 0 +var _restore_data_setup_after_picker: bool = false var _application_initialized := false var _pending_existing_root_path := "" func _ready() -> void: + if not _settings_manager.settings_changed.is_connected( + _apply_runtime_settings + ): + _settings_manager.settings_changed.connect(_apply_runtime_settings) + _settings_manager.load_settings() + _interface_fonts.set_readable_font_enabled( + _settings_manager.current_settings.use_readable_interface_font + ) if _data_root.resolve(): _configure_portable_stores() _initialize_after_data_root() @@ -377,6 +388,7 @@ func _initialize_after_data_root() -> void: _player_identity, _host_identity, _network_session, + _interface_fonts, ) _data_root.conflict_detected.connect(_on_portable_conflict) _data_root.status_changed.connect(_on_data_root_status) @@ -400,10 +412,6 @@ func _initialize_after_data_root() -> void: _test_world.get_player_water_triggers(), _test_world.get_safe_respawn_points() ) - if not _settings_manager.settings_changed.is_connected( - _apply_runtime_settings - ): - _settings_manager.settings_changed.connect(_apply_runtime_settings) _ui_pixelation.effective_pixel_size_changed.connect( _on_effective_ui_pixel_size_changed ) @@ -420,7 +428,6 @@ func _initialize_after_data_root() -> void: _game_ui.focus_open_settings_back_button ) _pixelation_reset.reset_requested.connect(_reset_pixelation) - _settings_manager.load_settings() _apply_runtime_settings(_settings_manager.current_settings) _set_gameplay_active(false) var title_screen: TitleScreenType = _game_ui.get_title_screen() @@ -491,9 +498,10 @@ func _show_data_root_setup() -> void: "Keep Current Location", false, "current" ) _data_setup_dialog.custom_action.connect(_on_data_setup_action) + _interface_fonts.apply_utility_theme(_data_setup_dialog) add_child(_data_setup_dialog) - var default_path := _data_root.default_visible_path() - var legacy := PortableDataMigration.legacy_files_present() + var default_path: String = _data_root.default_visible_path() + var legacy: bool = PortableDataMigration.legacy_files_present() _data_setup_dialog.dialog_text = ( ("Move your existing NETFISHING data to an easy-to-find folder." if legacy else "Choose where NETFISHING stores your player data.") @@ -512,7 +520,7 @@ func _show_data_root_setup() -> void: func _use_default_data_root() -> void: - var path := _data_root.default_visible_path() + var path: String = _data_root.default_visible_path() if path.is_empty(): _show_folder_picker() return @@ -535,7 +543,29 @@ func _show_folder_picker() -> void: _data_folder_dialog.access = FileDialog.ACCESS_FILESYSTEM _data_folder_dialog.use_native_dialog = false _data_folder_dialog.dir_selected.connect(_activate_selected_data_path) + _data_folder_dialog.canceled.connect(_on_data_folder_picker_canceled) + _interface_fonts.apply_utility_theme(_data_folder_dialog) add_child(_data_folder_dialog) + if _data_folder_dialog.visible: + return + _data_folder_picker_generation += 1 + var generation: int = _data_folder_picker_generation + _restore_data_setup_after_picker = ( + _data_setup_dialog != null and _data_setup_dialog.visible + ) + if _data_setup_dialog != null: + _data_setup_dialog.hide() + _open_folder_picker_after_modal.call_deferred(generation) + + +func _open_folder_picker_after_modal(generation: int) -> void: + await get_tree().process_frame + if ( + generation != _data_folder_picker_generation + or _data_folder_dialog == null + or _data_folder_dialog.visible + ): + return _data_folder_dialog.current_dir = ( _data_root.default_visible_path().get_base_dir() if not _data_root.default_visible_path().is_empty() @@ -544,10 +574,29 @@ func _show_folder_picker() -> void: _data_folder_dialog.popup_centered_ratio(0.75) +func _on_data_folder_picker_canceled() -> void: + _data_folder_picker_generation += 1 + var generation: int = _data_folder_picker_generation + var should_restore_setup: bool = _restore_data_setup_after_picker + _restore_data_setup_after_picker = false + if _data_folder_dialog != null: + _data_folder_dialog.hide() + if should_restore_setup: + _restore_data_setup_after_picker_after_modal.call_deferred(generation) + + +func _restore_data_setup_after_picker_after_modal(generation: int) -> void: + await get_tree().process_frame + if generation != _data_folder_picker_generation: + return + _show_data_root_setup() + + func _activate_selected_data_path(path: String, app_data: bool = false) -> void: - var ok := false + _restore_data_setup_after_picker = false + var ok: bool = false if PortableDataMigration.legacy_files_present(): - var result := ( + var result: Dictionary = ( PortableDataMigration.adopt_legacy_app_data(_data_root) if app_data else PortableDataMigration.migrate_legacy_to(_data_root, path) @@ -571,7 +620,7 @@ func _activate_selected_data_path(path: String, app_data: bool = false) -> void: func _show_existing_root_choice(path: String) -> void: _pending_existing_root_path = path - var dialog := ConfirmationDialog.new() + var dialog: ConfirmationDialog = ConfirmationDialog.new() dialog.title = "Existing NETFISHING data" dialog.ok_button_text = "Use Data Already in Selected Folder" dialog.cancel_button_text = "Cancel" @@ -594,7 +643,7 @@ func _show_existing_root_choice(path: String) -> void: dialog.custom_action.connect(func(action: StringName) -> void: if action != &"replace": return - var result := PortableDataMigration.replace_existing_with_legacy( + var result: Dictionary = PortableDataMigration.replace_existing_with_legacy( _data_root, _pending_existing_root_path ) if bool(result.get("ok", false)): @@ -605,6 +654,7 @@ func _show_existing_root_choice(path: String) -> void: else: _show_data_error(str(result.get("message", "Migration failed."))) ) + _interface_fonts.apply_utility_theme(dialog) add_child(dialog) dialog.popup_centered(Vector2i(680, 360)) @@ -616,7 +666,8 @@ func _show_data_error(message: String) -> void: func _on_portable_conflict(message: String, _path: String) -> void: - var dialog := AcceptDialog.new() + var dialog: AcceptDialog = AcceptDialog.new() + _interface_fonts.apply_utility_theme(dialog) dialog.title = "Player data conflict" dialog.dialog_text = ( message @@ -635,7 +686,8 @@ func _on_portable_conflict(message: String, _path: String) -> void: func _on_data_root_status(message: String) -> void: if "Syncthing conflict" not in message: return - var dialog := AcceptDialog.new() + var dialog: AcceptDialog = AcceptDialog.new() + _interface_fonts.apply_utility_theme(dialog) dialog.title = "Synced data needs review" dialog.dialog_text = message dialog.add_button("Open Data Folder", false, "open") @@ -771,6 +823,9 @@ func _process(_delta: float) -> void: func _apply_runtime_settings(settings: PlayerSettingsType) -> void: if settings == null: return + _interface_fonts.set_readable_font_enabled( + settings.use_readable_interface_font + ) _apply_world_pixelation(settings.world_pixel_size) _ui_pixelation.set_pixel_size(settings.ui_pixel_size) _game_ui.get_title_screen().set_world_pixelation( diff --git a/main/main.tscn b/main/main.tscn index 576dc80..89c988f 100644 --- a/main/main.tscn +++ b/main/main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=39 format=3] +[gd_scene load_steps=40 format=3] [ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"] [ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"] @@ -38,6 +38,7 @@ [ext_resource type="Script" path="res://network/network_player_list_service.gd" id="36_player_list"] [ext_resource type="Script" path="res://network/player_data_root.gd" id="37_data_root"] [ext_resource type="Script" path="res://network/identity_backup_service.gd" id="38_identity_backup"] +[ext_resource type="Script" path="res://ui/interface_font_controller.gd" id="39_fonts"] [node name="Main" type="Node3D"] script = ExtResource("3_main") @@ -46,6 +47,10 @@ pelican_buyer_profile = ExtResource("7_pelicans") main_shop_buyer_profile = ExtResource("12_main_shop") item_catalog = ExtResource("11_items") +[node name="InterfaceFontController" type="Node" parent="."] +unique_name_in_owner = true +script = ExtResource("39_fonts") + [node name="PlayerDataRoot" type="Node" parent="."] unique_name_in_owner = true script = ExtResource("37_data_root") diff --git a/network/host_ban_store.gd b/network/host_ban_store.gd index 716a60e..f8c4bf9 100644 --- a/network/host_ban_store.gd +++ b/network/host_ban_store.gd @@ -24,11 +24,15 @@ func is_banned(host_fingerprint: String, target_fingerprint: String) -> bool: return Dictionary(_namespaces.get(host_fingerprint, {})).has(target_fingerprint) -func ban(host_fingerprint: String, target_fingerprint: String, name: String) -> bool: +func ban( + host_fingerprint: String, + target_fingerprint: String, + display_name: String, +) -> bool: if ( not NetworkIdentityCrypto.valid_fingerprint(host_fingerprint) or not NetworkIdentityCrypto.valid_fingerprint(target_fingerprint) - or not NetworkProfilePreferences.is_valid_display_name(name) + or not NetworkProfilePreferences.is_valid_display_name(display_name) ): return false _ensure_loaded() @@ -41,7 +45,7 @@ func ban(host_fingerprint: String, target_fingerprint: String, name: String) -> records[target_fingerprint] = { "host_fingerprint": host_fingerprint, "target_fingerprint": target_fingerprint, - "last_known_display_name": name, + "last_known_display_name": display_name, "banned_unix": int(Time.get_unix_time_from_system()), "reason": "host_ban", } diff --git a/network/identity_backup_service.gd b/network/identity_backup_service.gd index 98f6106..bceb4bb 100644 --- a/network/identity_backup_service.gd +++ b/network/identity_backup_service.gd @@ -25,12 +25,12 @@ func setup( func default_export_path(identity_type: String) -> String: - var store := _store(identity_type) + var store: LocalSigningIdentityStore = _store(identity_type) if store == null: return "" if not store.is_ready() and not store.load_or_create(): return "" - var timestamp := Time.get_datetime_string_from_system().replace(":", "-") + var timestamp: String = Time.get_datetime_string_from_system().replace(":", "-") return _data_root.identity_backup_directory().path_join( "%s-%s-%s.nfidentity" % [ identity_type, @@ -50,14 +50,14 @@ func export_backup( return _finish(false, "Passphrases must match and contain at least 12 characters.") if FileAccess.file_exists(path): return _finish(false, "Choose a new backup filename.") - var store := _store(identity_type) + var store: LocalSigningIdentityStore = _store(identity_type) if store == null or (not store.is_ready() and not store.load_or_create()): return _finish(false, "The active identity is unavailable.") - var material := store.export_identity_material() - var proof := store.sign("identity_backup_self_test", [ + var material: Dictionary = store.export_identity_material() + var proof: PackedByteArray = store.sign("identity_backup_self_test", [ identity_type, material["fingerprint"], ]) - var envelope := { + var envelope: Dictionary = { "magic": MAGIC, "format_version": FORMAT_VERSION, "identity_type": identity_type, @@ -70,16 +70,18 @@ func export_backup( "self_signature": Marshalls.raw_to_base64(proof), } DirAccess.make_dir_recursive_absolute(path.get_base_dir()) - var file := FileAccess.open_encrypted_with_pass(path, FileAccess.WRITE, passphrase) + var file: FileAccess = FileAccess.open_encrypted_with_pass( + path, FileAccess.WRITE, passphrase + ) if file == null: return _finish(false, "Could not write this identity backup.") file.store_string(JSON.stringify(envelope)) file.flush() - var ok := file.get_error() == OK + var ok: bool = file.get_error() == OK file.close() if not ok: return _finish(false, "Could not write this identity backup.") - var verified := inspect_backup(path, passphrase, identity_type) + var verified: Dictionary = inspect_backup(path, passphrase, identity_type) if not bool(verified.get("ok", false)): DirAccess.remove_absolute(path) return _finish(false, "Could not verify this identity backup.") @@ -97,12 +99,14 @@ func inspect_backup( or FileAccess.get_size(path) > MAX_BACKUP_BYTES ): return {"ok": false} - var file := FileAccess.open_encrypted_with_pass(path, FileAccess.READ, passphrase) + var file: FileAccess = FileAccess.open_encrypted_with_pass( + path, FileAccess.READ, passphrase + ) if file == null: return {"ok": false} - var text := file.get_as_text() + var text: String = file.get_as_text() file.close() - var json := JSON.new() + var json: JSON = JSON.new() if json.parse(text) != OK or typeof(json.data) != TYPE_DICTIONARY: return {"ok": false} var data: Dictionary = json.data @@ -113,21 +117,23 @@ func inspect_backup( or data.get("algorithm") != NetworkIdentityCrypto.ALGORITHM ): return {"ok": false} - var private_pem := str(data.get("private_pem", "")) - var public_pem := NetworkIdentityCrypto.normalize_public_pem( + var private_pem: String = str(data.get("private_pem", "")) + var public_pem: String = NetworkIdentityCrypto.normalize_public_pem( str(data.get("public_pem", "")) ) - var fingerprint := str(data.get("fingerprint", "")) + var fingerprint: String = str(data.get("fingerprint", "")) if ( private_pem.length() > 128 * 1024 or public_pem.length() > 32 * 1024 or NetworkIdentityCrypto.fingerprint_public_pem(public_pem) != fingerprint ): return {"ok": false} - var key := CryptoKey.new() + var key: CryptoKey = CryptoKey.new() if key.load_from_string(private_pem) != OK: return {"ok": false} - var signature := Marshalls.base64_to_raw(str(data.get("self_signature", ""))) + var signature: PackedByteArray = Marshalls.base64_to_raw( + str(data.get("self_signature", "")) + ) if not NetworkIdentityCrypto.verify_fields( NetworkIdentityCrypto.load_public_key(public_pem), "identity_backup_self_test", @@ -135,7 +141,7 @@ func inspect_backup( signature, ): return {"ok": false} - var fresh := NetworkIdentityCrypto.sign_fields( + var fresh: PackedByteArray = NetworkIdentityCrypto.sign_fields( key, "identity_import_self_test", [fingerprint] ) if not NetworkIdentityCrypto.verify_fields( @@ -160,12 +166,12 @@ func import_backup( passphrase: String, confirmed_replacement: bool, ) -> Dictionary: - var inspected := inspect_backup(path, passphrase, identity_type) + var inspected: Dictionary = inspect_backup(path, passphrase, identity_type) if not bool(inspected.get("ok", false)): _finish(false, "Could not open this identity backup.") return {"ok": false} - var store := _store(identity_type) - var incoming := str(inspected["fingerprint"]) + var store: LocalSigningIdentityStore = _store(identity_type) + var incoming: String = str(inspected["fingerprint"]) if store.fingerprint == incoming: _finish(true, "This identity is already active.") return {"ok": true, "same": true} @@ -176,7 +182,7 @@ func import_backup( "current_fingerprint": store.fingerprint, "incoming_fingerprint": incoming, } - var result := store.install_identity_material( + var result: Dictionary = store.install_identity_material( str(inspected["private_pem"]), str(inspected["public_pem"]), incoming, diff --git a/network/network_chat_service.gd b/network/network_chat_service.gd index fcf3ff7..36f26b6 100644 --- a/network/network_chat_service.gd +++ b/network/network_chat_service.gd @@ -245,10 +245,10 @@ func _on_peer_removed(peer_id: int) -> void: _rate_times.erase(peer_id) if not _session.is_host(): return - var name: String = _peer_names.get(peer_id, "Player") + var display_name: String = _peer_names.get(peer_id, "Player") _peer_names.erase(peer_id) _broadcast(_make_message( - NetworkChatProtocol.Kind.SYSTEM, 0, "", "%s left." % name + NetworkChatProtocol.Kind.SYSTEM, 0, "", "%s left." % display_name )) diff --git a/network/network_mail_service.gd b/network/network_mail_service.gd index f4f3456..b2fa672 100644 --- a/network/network_mail_service.gd +++ b/network/network_mail_service.gd @@ -144,14 +144,14 @@ func get_recipient_choices() -> Array[Dictionary]: var normalized := str(choice["name"]).to_lower() counts[normalized] = int(counts.get(normalized, 0)) + 1 for choice: Dictionary in choices: - var name: String = choice["name"] - if int(counts.get(name.to_lower(), 0)) > 1: + var display_name: String = choice["name"] + if int(counts.get(display_name.to_lower(), 0)) > 1: choice["label"] = "%s ยท %s" % [ - name, + display_name, NetworkIdentityCrypto.compact_suffix(choice["fingerprint"]), ] else: - choice["label"] = name + choice["label"] = display_name return choices @@ -531,8 +531,8 @@ func prepare_recipient(transfer_id: String, letter: Dictionary) -> void: func _prepare_recipient(transfer_id: String, letter: Dictionary) -> void: - var ready := _can_receive(letter["attachment"]) - _send_phase_ack("recipient_prepared", transfer_id, ready) + var recipient_ready: bool = _can_receive(letter["attachment"]) + _send_phase_ack("recipient_prepared", transfer_id, recipient_ready) func _request_sender_commit(peer_id: int, transfer_id: String, letter: Dictionary) -> void: diff --git a/network/network_player_list_service.gd b/network/network_player_list_service.gd index 2681a14..9145cfa 100644 --- a/network/network_player_list_service.gd +++ b/network/network_player_list_service.gd @@ -117,12 +117,17 @@ func kick(peer_id: int, fingerprint: String, revision: int) -> bool: return ok -func ban(peer_id: int, fingerprint: String, name: String, revision: int) -> bool: +func ban( + peer_id: int, + fingerprint: String, + display_name: String, + revision: int, +) -> bool: if not _valid_moderation_target(peer_id, fingerprint, revision): moderation_finished.emit(false, "That player is no longer connected.") return false var host_fingerprint := _session.get_host_identity_fingerprint() - if not _bans.ban(host_fingerprint, fingerprint, name): + if not _bans.ban(host_fingerprint, fingerprint, display_name): moderation_finished.emit(false, "Ban could not be saved.") return false var ok := _session.kick_authenticated_peer(peer_id, fingerprint, true) diff --git a/network/network_profile_service.gd b/network/network_profile_service.gd index 40b15f4..2666d26 100644 --- a/network/network_profile_service.gd +++ b/network/network_profile_service.gd @@ -172,10 +172,14 @@ func submit_profile_apply(data: Dictionary) -> void: func _process_apply_request(peer_id: int, data: Dictionary) -> void: - var name := str(data["display_name"]).strip_edges() - var conflict := _name_conflicts(peer_id, name) - var accepted := not conflict or bool(data["use_anyway"]) - var suggestions := _make_suggestions(peer_id, name) if conflict else PackedStringArray() + var display_name: String = str(data["display_name"]).strip_edges() + var conflict: bool = _name_conflicts(peer_id, display_name) + var accepted: bool = not conflict or bool(data["use_anyway"]) + var suggestions: PackedStringArray = ( + _make_suggestions(peer_id, display_name) + if conflict + else PackedStringArray() + ) if peer_id == _session.get_local_peer_id(): _apply_local_result( str(data["request_id"]), accepted, "", conflict, suggestions @@ -184,7 +188,7 @@ func _process_apply_request(peer_id: int, data: Dictionary) -> void: if accepted: _host_pending_apply[str(data["request_id"])] = { "peer_id": peer_id, - "display_name": name, + "display_name": display_name, "appearance": Dictionary(data["appearance"]).duplicate(true), "authorization": { "request_id": data["request_id"], @@ -214,10 +218,10 @@ func confirm_profile_saved(request_id: String) -> void: ): return _host_pending_apply.erase(request_id) - var name := str(pending["display_name"]) - var appearance := Dictionary(pending["appearance"]) - _session.apply_canonical_profile(sender_id, name, appearance) - var record := _session.get_peer_record(sender_id) + var display_name: String = str(pending["display_name"]) + var appearance: Dictionary = Dictionary(pending["appearance"]) + _session.apply_canonical_profile(sender_id, display_name, appearance) + var record: PeerRegistry.PeerRecord = _session.get_peer_record(sender_id) if record != null: record.profile_authorization = pending.get("authorization", {}).duplicate(true) _apply_to_avatar(sender_id, appearance) diff --git a/network/network_session.gd b/network/network_session.gd index 3844ea3..1538550 100644 --- a/network/network_session.gd +++ b/network/network_session.gd @@ -1145,13 +1145,13 @@ func _apply_spawn_entry(entry: Dictionary) -> void: ) _registry.update_appearance(peer_id, appearance) var transform: Transform3D = _spawn_service.get_spawn_transform_for_index(0) - var position: Array = entry["position"] - if position.size() != 3: + var spawn_position: Array = entry["position"] + if spawn_position.size() != 3: return transform.origin = Vector3( - float(position[0]), - float(position[1]), - float(position[2]) + float(spawn_position[0]), + float(spawn_position[1]), + float(spawn_position[2]) ) transform.basis = Basis(Vector3.UP, float(entry["yaw"])) var avatar := _spawn_service.spawn_remote_player(peer_id, transform, false) diff --git a/network/player_data_root.gd b/network/player_data_root.gd index b2f8a65..9e379d2 100644 --- a/network/player_data_root.gd +++ b/network/player_data_root.gd @@ -34,7 +34,7 @@ var override_active := false func resolve() -> bool: _load_bootstrap_identity() - var command_line := _command_line_override() + var command_line: String = _command_line_override() if not command_line.is_empty(): override_active = true mode = Mode.COMMAND_LINE_OVERRIDE @@ -42,18 +42,18 @@ func resolve() -> bool: if OS.has_environment(ENVIRONMENT_VARIABLE): override_active = true mode = Mode.ENVIRONMENT_OVERRIDE - var environment_path := OS.get_environment(ENVIRONMENT_VARIABLE) + var environment_path: String = OS.get_environment(ENVIRONMENT_VARIABLE) if not environment_path.is_absolute_path(): return _fail("NETFISHING_DATA_DIR must be an absolute path.") return _activate_existing(environment_path, "", false) - var bootstrap := _read_json(BOOTSTRAP_PATH, 64 * 1024) + var bootstrap: Dictionary = _read_json(BOOTSTRAP_PATH, 64 * 1024) if bootstrap.is_empty() and FileAccess.file_exists(BOOTSTRAP_PATH + ".backup"): bootstrap = _read_json(BOOTSTRAP_PATH + ".backup", 64 * 1024) if not bootstrap.is_empty(): if bootstrap.get("format_version") != BOOTSTRAP_VERSION: return _fail("The data-folder pointer uses an unsupported version.") - var selected := str(bootstrap.get("selected_absolute_path", "")) - var expected := str(bootstrap.get("expected_root_id", "")) + var selected: String = str(bootstrap.get("selected_absolute_path", "")) + var expected: String = str(bootstrap.get("expected_root_id", "")) if selected.is_empty(): return _fail("The data-folder pointer is incomplete.") mode = ( @@ -68,7 +68,7 @@ func resolve() -> bool: func default_visible_path() -> String: - var documents := 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 "" @@ -77,14 +77,14 @@ func select_new_root(path: String, app_data: bool = false) -> bool: return _fail("The data folder is controlled by a process override.") if device_id.length() != 32: device_id = Crypto.new().generate_random_bytes(16).hex_encode() - var normalized := _normalize(path) + var normalized: String = _normalize(path) if app_data: normalized = ProjectSettings.globalize_path(APP_DATA_PORTABLE_PATH) if not _validate_candidate(normalized, true): return false - var manifest_path := normalized.path_join(MANIFEST_FILENAME) + var manifest_path: String = normalized.path_join(MANIFEST_FILENAME) if FileAccess.file_exists(manifest_path): - var manifest := _read_json(manifest_path) + var manifest: Dictionary = _read_json(manifest_path) if not _valid_manifest(manifest): return _fail("The selected folder has a malformed NETFISHING manifest.") root_id = str(manifest["root_id"]) @@ -107,30 +107,30 @@ func select_new_root(path: String, app_data: bool = false) -> bool: func create_unbound_root(path: String) -> Dictionary: - var normalized := _normalize(path) + var normalized: String = _normalize(path) if not _validate_candidate(normalized, true): return {"ok": false, "message": error_message} if not _directory_is_empty(normalized): return {"ok": false, "message": "The destination staging folder is not empty."} - var id := Crypto.new().generate_random_bytes(16).hex_encode() + var id: String = Crypto.new().generate_random_bytes(16).hex_encode() if not _create_layout(normalized, id): return {"ok": false, "message": "The portable layout could not be created."} return {"ok": true, "root_id": id} func create_app_data_layout_for_migration(path: String) -> Dictionary: - var normalized := _normalize(path) + var normalized: String = _normalize(path) if not _validate_candidate(normalized, false): return {"ok": false, "message": error_message} - var manifest_path := normalized.path_join(MANIFEST_FILENAME) + var manifest_path: String = normalized.path_join(MANIFEST_FILENAME) if FileAccess.file_exists(manifest_path): - var manifest := _read_json(manifest_path) + var manifest: Dictionary = _read_json(manifest_path) return ( {"ok": true, "root_id": str(manifest.get("root_id", ""))} if _valid_manifest(manifest) else {"ok": false, "message": "The app-data manifest is malformed."} ) - var id := Crypto.new().generate_random_bytes(16).hex_encode() + var id: String = Crypto.new().generate_random_bytes(16).hex_encode() if not _create_layout(normalized, id): return {"ok": false, "message": "The app-data layout could not be created."} return {"ok": true, "root_id": id} @@ -141,7 +141,7 @@ func use_existing_root(path: String) -> bool: return _fail("The data folder is controlled by a process override.") if device_id.length() != 32: device_id = Crypto.new().generate_random_bytes(16).hex_encode() - var normalized := _normalize(path) + var normalized: String = _normalize(path) if not _activate_existing(normalized, "", true): return false if not _write_bootstrap(root_path, root_id): @@ -156,7 +156,7 @@ func use_existing_root(path: String) -> bool: return true -func path_for(owner: StringName) -> String: +func path_for(store_owner: StringName) -> String: var relative: String = { &"player_save": "player/player_save.json", &"network_profile": "player/network_profile.json", @@ -166,7 +166,7 @@ func path_for(owner: StringName) -> String: &"player_relationships": "social/player_relationships.json", &"server_trust": "social/server_trust.json", &"host_bans": "social/host_bans.json", - }.get(owner, "") + }.get(store_owner, "") return root_path.path_join(relative) if not relative.is_empty() else "" @@ -202,16 +202,16 @@ func report_conflict(message: String, conflict_path: String) -> void: func _activate_existing(path: String, expected_id: String, permit_creation: bool) -> bool: - var normalized := _normalize(path) + var normalized: String = _normalize(path) if not _validate_candidate(normalized, permit_creation): return false - var manifest_path := normalized.path_join(MANIFEST_FILENAME) + var manifest_path: String = normalized.path_join(MANIFEST_FILENAME) if not FileAccess.file_exists(manifest_path): return _fail("The selected folder is not a NETFISHING data folder.") - var manifest := _read_json(manifest_path) + var manifest: Dictionary = _read_json(manifest_path) if not _valid_manifest(manifest): return _fail("The NETFISHING data-folder manifest is malformed.") - var found_id := str(manifest["root_id"]) + var found_id: String = str(manifest["root_id"]) if not expected_id.is_empty() and expected_id != found_id: return _fail("The selected data folder does not match this device pointer.") if not _test_writable(normalized): @@ -228,7 +228,7 @@ func _activate_existing(path: String, expected_id: String, permit_creation: bool func _validate_candidate(path: String, create: bool) -> bool: if path.is_empty() or not path.is_absolute_path(): return _fail("Choose an absolute filesystem folder.") - var project := ProjectSettings.globalize_path("res://").trim_suffix("/") + var project: String = ProjectSettings.globalize_path("res://").trim_suffix("/") if path == project or path.begins_with(project + "/"): return _fail("The project folder cannot be used as the player data folder.") if not DirAccess.dir_exists_absolute(path): @@ -238,8 +238,10 @@ func _validate_candidate(path: String, create: bool) -> bool: func _test_writable(path: String) -> bool: - var probe := path.path_join(".netfishing-write-%s.tmp" % device_id.left(12)) - var file := FileAccess.open(probe, FileAccess.WRITE) + var probe: String = path.path_join( + ".netfishing-write-%s.tmp" % device_id.left(12) + ) + var file: FileAccess = FileAccess.open(probe, FileAccess.WRITE) if file == null: return false file.store_string("probe") @@ -254,8 +256,8 @@ func _create_layout(path: String, id: String) -> bool: ]: if DirAccess.make_dir_recursive_absolute(path.path_join(relative)) != OK: return false - var now := int(Time.get_unix_time_from_system()) - var manifest := { + var now: int = int(Time.get_unix_time_from_system()) + var manifest: Dictionary = { "format_version": MANIFEST_VERSION, "layout_version": LAYOUT_VERSION, "application": APPLICATION_ID, @@ -267,7 +269,7 @@ func _create_layout(path: String, id: String) -> bool: path.path_join(MANIFEST_FILENAME), JSON.stringify(manifest, "\t") ): return false - var readme := ( + var readme: String = ( "NETFISHING player data\n\n" + "This folder is safe to synchronize with tools such as Syncthing.\n" + "Active private identity keys remain device-local.\n" @@ -280,8 +282,8 @@ func _create_layout(path: String, id: String) -> bool: func _write_bootstrap(path: String, id: String) -> bool: - var now := int(Time.get_unix_time_from_system()) - var data := { + var now: int = int(Time.get_unix_time_from_system()) + var data: Dictionary = { "format_version": BOOTSTRAP_VERSION, "selected_absolute_path": path, "expected_root_id": id, @@ -296,7 +298,7 @@ func _write_bootstrap(path: String, id: String) -> bool: func _load_bootstrap_identity() -> void: - var data := _read_json(BOOTSTRAP_PATH, 64 * 1024) + var data: Dictionary = _read_json(BOOTSTRAP_PATH, 64 * 1024) if data.is_empty(): data = _read_json(BOOTSTRAP_PATH + ".backup", 64 * 1024) device_id = str(data.get("device_id", "")) @@ -305,9 +307,9 @@ func _load_bootstrap_identity() -> void: func _command_line_override() -> String: - var args := OS.get_cmdline_user_args() + var args: PackedStringArray = OS.get_cmdline_user_args() for index: int in args.size(): - var value := args[index] + var value: String = args[index] if value.begins_with("--data-dir="): return value.trim_prefix("--data-dir=") if value == "--data-dir" and index + 1 < args.size(): @@ -326,15 +328,15 @@ func _valid_manifest(data: Dictionary) -> bool: func _directory_is_empty(path: String) -> bool: - var access := DirAccess.open(path) + var access: DirAccess = DirAccess.open(path) if access == null: return true access.list_dir_begin() - var name := access.get_next() - while name in [".", ".."]: - name = access.get_next() + var entry_name: String = access.get_next() + while entry_name in [".", ".."]: + entry_name = access.get_next() access.list_dir_end() - return name.is_empty() + return entry_name.is_empty() func _normalize(path: String) -> String: @@ -344,37 +346,37 @@ func _normalize(path: String) -> String: func _read_json(path: String, maximum := 1024 * 1024) -> Dictionary: if not FileAccess.file_exists(path): return {} - var file := FileAccess.open(path, FileAccess.READ) + var file: FileAccess = FileAccess.open(path, FileAccess.READ) if file == null or file.get_length() > maximum: return {} - var json := JSON.new() - var error := json.parse(file.get_as_text()) + var json: JSON = JSON.new() + var error: Error = json.parse(file.get_as_text()) file.close() return json.data if error == OK and typeof(json.data) == TYPE_DICTIONARY else {} func _write_text_atomic(path: String, text: String) -> bool: - var absolute := ( + var absolute: String = ( ProjectSettings.globalize_path(path) if path.begins_with("user://") else path ) if DirAccess.make_dir_recursive_absolute(absolute.get_base_dir()) != OK: return false - var temporary := absolute + ".tmp" - var file := FileAccess.open(temporary, FileAccess.WRITE) + var temporary: String = absolute + ".tmp" + var file: FileAccess = FileAccess.open(temporary, FileAccess.WRITE) if file == null: return false file.store_string(text) file.flush() - var ok := file.get_error() == OK + var ok: bool = file.get_error() == OK file.close() if not ok: return false - var backup := absolute + ".backup" + var backup: String = absolute + ".backup" if FileAccess.file_exists(backup): DirAccess.remove_absolute(backup) - var had_primary := FileAccess.file_exists(absolute) + var had_primary: bool = FileAccess.file_exists(absolute) if had_primary and DirAccess.rename_absolute(absolute, backup) != OK: DirAccess.remove_absolute(temporary) return false diff --git a/network/player_spawn_service.gd b/network/player_spawn_service.gd index c31feb6..1352758 100644 --- a/network/player_spawn_service.gd +++ b/network/player_spawn_service.gd @@ -78,10 +78,10 @@ func get_avatar(peer_id: int) -> Player: return _avatars.get(peer_id) -func set_peer_presentation_visible(peer_id: int, visible: bool) -> void: +func set_peer_presentation_visible(peer_id: int, should_be_visible: bool) -> void: var avatar: Player = _avatars.get(peer_id) if avatar != null and is_instance_valid(avatar): - avatar.set_remote_presentation_visible(visible) + avatar.set_remote_presentation_visible(should_be_visible) func get_peer_ids() -> Array[int]: diff --git a/network/portable_data_migration.gd b/network/portable_data_migration.gd index 7ab2b94..45ce7fb 100644 --- a/network/portable_data_migration.gd +++ b/network/portable_data_migration.gd @@ -24,9 +24,9 @@ static func migrate_legacy_to( data_root: PlayerDataRoot, destination: String, ) -> Dictionary: - var normalized := destination.simplify_path().trim_suffix("/") + var normalized: String = destination.simplify_path().trim_suffix("/") if DirAccess.dir_exists_absolute(normalized): - var existing_manifest := normalized.path_join( + var existing_manifest: String = normalized.path_join( PlayerDataRoot.MANIFEST_FILENAME ) if FileAccess.file_exists(existing_manifest): @@ -40,24 +40,24 @@ static func migrate_legacy_to( "ok": false, "message": "Choose an empty folder or create a NETFISHING subfolder.", } - var staging := "%s.migration-%s" % [ + var staging: String = "%s.migration-%s" % [ normalized, Crypto.new().generate_random_bytes(8).hex_encode() ] if DirAccess.make_dir_recursive_absolute(staging) != OK: return {"ok": false, "message": "Migration staging could not be created."} - var created := data_root.create_unbound_root(staging) + var created: Dictionary = data_root.create_unbound_root(staging) if not bool(created.get("ok", false)): _remove_tree(staging) return {"ok": false, "message": str(created.get("message", ""))} var copied: Array[String] = [] for source_name: String in LEGACY_FILES: - var source := ProjectSettings.globalize_path( + var source: String = ProjectSettings.globalize_path( "user://".path_join(source_name) ) if not FileAccess.file_exists(source): continue - var target := staging.path_join(LEGACY_FILES[source_name]) - var result := _copy_verified_json(source, target) + var target: String = staging.path_join(str(LEGACY_FILES[source_name])) + var result: Dictionary = _copy_verified_json(source, target) if not bool(result.get("ok", false)): _remove_tree(staging) return { @@ -70,11 +70,15 @@ static func migrate_legacy_to( if DirAccess.rename_absolute(staging, normalized) != OK: _remove_tree(staging) return {"ok": false, "message": "Migration could not activate its destination."} - var manifest := _read_json(normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME)) - var migrated_root_id := str(manifest.get("root_id", "")) + var manifest: Dictionary = _read_json( + normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME) + ) + var migrated_root_id: String = str(manifest.get("root_id", "")) if migrated_root_id.is_empty() or not data_root.use_existing_root(normalized): return {"ok": false, "message": "Migration completed but could not switch roots."} - var recovery := ProjectSettings.globalize_path("user://migration-recovery").path_join( + var recovery: String = ProjectSettings.globalize_path( + "user://migration-recovery" + ).path_join( Time.get_datetime_string_from_system().replace(":", "-") ) DirAccess.make_dir_recursive_absolute(recovery) @@ -94,7 +98,7 @@ static func migrate_active_to( data_root: PlayerDataRoot, destination: String, ) -> Dictionary: - var normalized := destination.simplify_path().trim_suffix("/") + var normalized: String = destination.simplify_path().trim_suffix("/") if normalized == data_root.root_path: return {"ok": true, "message": "This data folder is already active."} if DirAccess.dir_exists_absolute(normalized) and not _directory_empty(normalized): @@ -108,12 +112,12 @@ static func migrate_active_to( ), } return {"ok": false, "message": "Choose an empty folder or a NETFISHING subfolder."} - var staging := "%s.migration-%s" % [ + var staging: String = "%s.migration-%s" % [ normalized, Crypto.new().generate_random_bytes(8).hex_encode() ] if DirAccess.make_dir_recursive_absolute(staging) != OK: return {"ok": false, "message": "Migration staging could not be created."} - var created := data_root.create_unbound_root(staging) + var created: Dictionary = data_root.create_unbound_root(staging) if not bool(created.get("ok", false)): _remove_tree(staging) return {"ok": false, "message": str(created.get("message", ""))} @@ -127,7 +131,7 @@ static func migrate_active_to( "social/server_trust.json", "social/host_bans.json", ]: - var source := data_root.root_path.path_join(relative) + var source: String = data_root.root_path.path_join(relative) if not FileAccess.file_exists(source): continue if not bool(_copy_verified_json(source, staging.path_join(relative)).get("ok", false)): @@ -138,7 +142,7 @@ static func migrate_active_to( if DirAccess.rename_absolute(staging, normalized) != OK: _remove_tree(staging) return {"ok": false, "message": "Migration could not activate its destination."} - var old_root := data_root.root_path + var old_root: String = data_root.root_path if not data_root.use_existing_root(normalized): return {"ok": false, "message": "Migration copied data but did not change the pointer."} return { @@ -149,18 +153,20 @@ static func migrate_active_to( static func adopt_legacy_app_data(data_root: PlayerDataRoot) -> Dictionary: - var source_root := ProjectSettings.globalize_path("user://").trim_suffix("/") - var root := ProjectSettings.globalize_path(PlayerDataRoot.APP_DATA_PORTABLE_PATH) + var source_root: String = ProjectSettings.globalize_path("user://").trim_suffix("/") + var root: String = ProjectSettings.globalize_path( + PlayerDataRoot.APP_DATA_PORTABLE_PATH + ) if DirAccess.make_dir_recursive_absolute(root) != OK: return {"ok": false, "message": "The app-data folder could not be created."} - var created := data_root.create_app_data_layout_for_migration(root) + var created: Dictionary = data_root.create_app_data_layout_for_migration(root) if not bool(created.get("ok", false)): return created for source_name: String in LEGACY_FILES: - var source := source_root.path_join(source_name) + var source: String = source_root.path_join(source_name) if not FileAccess.file_exists(source): continue - var target := root.path_join(LEGACY_FILES[source_name]) + var target: String = root.path_join(str(LEGACY_FILES[source_name])) if not bool(_copy_verified_json(source, target).get("ok", false)): return { "ok": false, @@ -179,17 +185,19 @@ static func replace_existing_with_legacy( data_root: PlayerDataRoot, destination: String, ) -> Dictionary: - var normalized := destination.simplify_path().trim_suffix("/") - var manifest := normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME) + var normalized: String = destination.simplify_path().trim_suffix("/") + var manifest: String = normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME) if not FileAccess.file_exists(manifest): return {"ok": false, "message": "The selected folder is not a NETFISHING data root."} - var recovery := ProjectSettings.globalize_path("user://migration-recovery").path_join( + var recovery: String = ProjectSettings.globalize_path( + "user://migration-recovery" + ).path_join( "replaced-root-" + Time.get_datetime_string_from_system().replace(":", "-") ) if not _copy_tree(normalized, recovery): return {"ok": false, "message": "The selected data could not be backed up."} _remove_tree(normalized) - var result := migrate_legacy_to(data_root, normalized) + var result: Dictionary = migrate_legacy_to(data_root, normalized) if bool(result.get("ok", false)): result["replaced_root_backup"] = recovery return result @@ -199,26 +207,28 @@ static func replace_existing_with_active( data_root: PlayerDataRoot, destination: String, ) -> Dictionary: - var normalized := destination.simplify_path().trim_suffix("/") + var normalized: String = destination.simplify_path().trim_suffix("/") if not FileAccess.file_exists(normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME)): return {"ok": false, "message": "The selected folder is not a NETFISHING data root."} - var recovery := ProjectSettings.globalize_path("user://migration-recovery").path_join( + var recovery: String = ProjectSettings.globalize_path( + "user://migration-recovery" + ).path_join( "replaced-root-" + Time.get_datetime_string_from_system().replace(":", "-") ) if not _copy_tree(normalized, recovery): return {"ok": false, "message": "The selected data could not be backed up."} _remove_tree(normalized) - var result := migrate_active_to(data_root, normalized) + var result: Dictionary = migrate_active_to(data_root, normalized) if bool(result.get("ok", false)): result["replaced_root_backup"] = recovery return result static func _copy_verified_json(source: String, destination: String) -> Dictionary: - var bytes := PortableFileGuard.read_bytes(source) + var bytes: PackedByteArray = PortableFileGuard.read_bytes(source) if bytes.is_empty() and FileAccess.get_open_error() != OK: return {"ok": false} - var json := JSON.new() + var json: JSON = JSON.new() if ( json.parse(bytes.get_string_from_utf8()) != OK or typeof(json.data) != TYPE_DICTIONARY @@ -229,7 +239,7 @@ static func _copy_verified_json(source: String, destination: String) -> Dictiona return {"ok": false} if not _write_bytes(destination, bytes): return {"ok": false} - var copied := PortableFileGuard.read_bytes(destination) + var copied: PackedByteArray = PortableFileGuard.read_bytes(destination) return { "ok": ( PortableFileGuard.hash_bytes(copied) @@ -240,7 +250,7 @@ static func _copy_verified_json(source: String, destination: String) -> Dictiona static func _valid_owned_data(filename: String, data: Dictionary) -> bool: if filename == "player_save.json": - var version := int(data.get("save_version", -1)) + var version: int = int(data.get("save_version", -1)) return version >= 1 and version <= PlayerSaveManager.SAVE_VERSION return ( filename not in LEGACY_FILES @@ -255,14 +265,14 @@ static func _copy_bytes(source: String, destination: String) -> bool: static func _copy_tree(source: String, destination: String) -> bool: if DirAccess.make_dir_recursive_absolute(destination) != OK: return false - var access := DirAccess.open(source) + var access: DirAccess = DirAccess.open(source) if access == null: return false access.list_dir_begin() - var name := access.get_next() + var name: String = access.get_next() while not name.is_empty(): - var from := source.path_join(name) - var to := destination.path_join(name) + var from: String = source.path_join(name) + var to: String = destination.path_join(name) if access.current_is_dir(): if not _copy_tree(from, to): access.list_dir_end() @@ -277,19 +287,19 @@ static func _copy_tree(source: String, destination: String) -> bool: static func _write_bytes(path: String, bytes: PackedByteArray) -> bool: DirAccess.make_dir_recursive_absolute(path.get_base_dir()) - var file := FileAccess.open(path, FileAccess.WRITE) + var file: FileAccess = FileAccess.open(path, FileAccess.WRITE) if file == null: return false file.store_buffer(bytes) file.flush() - var ok := file.get_error() == OK + var ok: bool = file.get_error() == OK file.close() return ok static func _read_json(path: String) -> Dictionary: - var bytes := PortableFileGuard.read_bytes(path) - var json := JSON.new() + var bytes: PackedByteArray = PortableFileGuard.read_bytes(path) + var json: JSON = JSON.new() return ( json.data if json.parse(bytes.get_string_from_utf8()) == OK @@ -299,23 +309,23 @@ static func _read_json(path: String) -> Dictionary: static func _directory_empty(path: String) -> bool: - var access := DirAccess.open(path) + var access: DirAccess = DirAccess.open(path) if access == null: return true access.list_dir_begin() - var name := access.get_next() + var name: String = access.get_next() access.list_dir_end() return name.is_empty() static func _remove_tree(path: String) -> void: - var access := DirAccess.open(path) + var access: DirAccess = DirAccess.open(path) if access == null: return access.list_dir_begin() - var name := access.get_next() + var name: String = access.get_next() while not name.is_empty(): - var child := path.path_join(name) + var child: String = path.path_join(name) if access.current_is_dir(): _remove_tree(child) else: diff --git a/network/portable_file_guard.gd b/network/portable_file_guard.gd index 95f21aa..cb99ae1 100644 --- a/network/portable_file_guard.gd +++ b/network/portable_file_guard.gd @@ -7,19 +7,21 @@ const MAX_PORTABLE_FILE_BYTES := 16 * 1024 * 1024 static func hash_file(path: String) -> String: if not FileAccess.file_exists(path): return "" - var file := FileAccess.open(path, FileAccess.READ) + var file: FileAccess = FileAccess.open(path, FileAccess.READ) if file == null or file.get_length() > MAX_PORTABLE_FILE_BYTES: return "" - var bytes := file.get_buffer(file.get_length()) + var bytes: PackedByteArray = file.get_buffer(file.get_length()) file.close() return hash_bytes(bytes) static func hash_bytes(bytes: PackedByteArray) -> String: - var context := HashingContext.new() - if context.start(HashingContext.HASH_SHA256) != OK: + var context: HashingContext = HashingContext.new() + var start_error: Error = context.start(HashingContext.HASH_SHA256) + if start_error != OK: return "" - if context.update(bytes) != OK: + var update_error: Error = context.update(bytes) + if update_error != OK: return "" return context.finish().hex_encode() @@ -27,10 +29,10 @@ static func hash_bytes(bytes: PackedByteArray) -> String: static func read_bytes(path: String, maximum_bytes: int = MAX_PORTABLE_FILE_BYTES) -> PackedByteArray: if not FileAccess.file_exists(path): return PackedByteArray() - var file := FileAccess.open(path, FileAccess.READ) + var file: FileAccess = FileAccess.open(path, FileAccess.READ) if file == null or file.get_length() > maximum_bytes: return PackedByteArray() - var bytes := file.get_buffer(file.get_length()) + var bytes: PackedByteArray = file.get_buffer(file.get_length()) file.close() return bytes @@ -42,10 +44,10 @@ static func write_guarded( conflict_directory: String, device_id: String, ) -> Dictionary: - var current_exists := FileAccess.file_exists(path) - var current_hash := hash_file(path) if current_exists else "" + var current_exists: bool = FileAccess.file_exists(path) + var current_hash: String = hash_file(path) if current_exists else "" if current_hash != expected_hash: - var conflict_path := _write_conflict_copy( + var conflict_path: String = _write_conflict_copy( path, bytes, conflict_directory, device_id ) return { @@ -60,14 +62,14 @@ static func write_guarded( } if not _ensure_parent(path): return {"ok": false, "conflict": false, "hash": expected_hash} - var temporary := path + ".tmp" - var backup := path + ".backup" - var file := FileAccess.open(temporary, FileAccess.WRITE) + var temporary: String = path + ".tmp" + var backup: String = path + ".backup" + var file: FileAccess = FileAccess.open(temporary, FileAccess.WRITE) if file == null: return {"ok": false, "conflict": false, "hash": expected_hash} file.store_buffer(bytes) file.flush() - var write_error := file.get_error() + var write_error: Error = file.get_error() file.close() if write_error != OK: _remove(temporary) @@ -90,13 +92,13 @@ static func write_guarded( static func has_syncthing_conflict(directory: String) -> bool: - var access := DirAccess.open(directory) + var access: DirAccess = DirAccess.open(directory) if access == null: return false access.list_dir_begin() - var name := access.get_next() + var name: String = access.get_next() while not name.is_empty(): - var lower := name.to_lower() + var lower: String = name.to_lower() if "sync-conflict" in lower or ".syncthing." in lower: access.list_dir_end() return true @@ -113,24 +115,24 @@ static func _write_conflict_copy( ) -> String: if DirAccess.make_dir_recursive_absolute(conflict_directory) != OK: return "" - var filename := canonical_path.get_file() - var safe_device := device_id.left(16) - var timestamp := Time.get_datetime_string_from_system().replace(":", "-") - var destination := conflict_directory.path_join( + var filename: String = canonical_path.get_file() + var safe_device: String = device_id.left(16) + var timestamp: String = Time.get_datetime_string_from_system().replace(":", "-") + var destination: String = conflict_directory.path_join( "%s.local-%s-%s" % [filename, safe_device, timestamp] ) - var file := FileAccess.open(destination, FileAccess.WRITE) + var file: FileAccess = FileAccess.open(destination, FileAccess.WRITE) if file == null: return "" file.store_buffer(bytes) file.flush() - var ok := file.get_error() == OK + var ok: bool = file.get_error() == OK file.close() return destination if ok else "" static func _ensure_parent(path: String) -> bool: - var parent := path.get_base_dir() + var parent: String = path.get_base_dir() return ( DirAccess.dir_exists_absolute(parent) or DirAccess.make_dir_recursive_absolute(parent) == OK diff --git a/network/saved_server_store.gd b/network/saved_server_store.gd index eadc281..de48c2c 100644 --- a/network/saved_server_store.gd +++ b/network/saved_server_store.gd @@ -323,9 +323,12 @@ func _validate_entry(value: Dictionary, is_saved: bool) -> Dictionary: var endpoint := EndpointParser.parse(endpoint_text) if not endpoint.is_valid(): return {} - var name: String = str(value.get("display_name", "")).strip_edges() + var display_name: String = str( + value.get("display_name", "") + ).strip_edges() if is_saved and ( - name.is_empty() or name.length() > MAX_DISPLAY_NAME_LENGTH + display_name.is_empty() + or display_name.length() > MAX_DISPLAY_NAME_LENGTH ): return {} var result: Dictionary = _make_entry( diff --git a/player/player.gd b/player/player.gd index 36a0ae9..e735557 100644 --- a/player/player.gd +++ b/player/player.gd @@ -425,19 +425,19 @@ func apply_network_teleport(snapshot: Dictionary) -> void: func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary: - var position: Variant = snapshot.get("position") + var snapshot_position: Variant = snapshot.get("position") var network_velocity: Variant = snapshot.get("velocity") if ( - typeof(position) != TYPE_ARRAY + typeof(snapshot_position) != TYPE_ARRAY or typeof(network_velocity) != TYPE_ARRAY - or position.size() != 3 + or snapshot_position.size() != 3 or network_velocity.size() != 3 ): return {} var parsed_position := Vector3( - float(position[0]), - float(position[1]), - float(position[2]) + float(snapshot_position[0]), + float(snapshot_position[1]), + float(snapshot_position[2]) ) var parsed_velocity := Vector3( float(network_velocity[0]), diff --git a/settings/player_settings.gd b/settings/player_settings.gd index 44c00a3..70cceb1 100644 --- a/settings/player_settings.gd +++ b/settings/player_settings.gd @@ -20,6 +20,7 @@ const UI_DESKTOP_RENDER_HEIGHTS: Array[int] = [0, 612, 504, 396, 288] const UI_COMPACT_RENDER_HEIGHTS: Array[int] = [0, 408, 336, 264, 192] @export var auto_click_enabled: bool = false +@export var use_readable_interface_font: bool = false @export_range(0.10, 0.50, 0.01) var auto_click_interval: float = 0.20 @export_range(0.001, 0.012, 0.0005) var mouse_camera_sensitivity: float = 0.005 @export_range(0.5, 5.0, 0.1) var controller_camera_sensitivity: float = 2.5 @@ -49,6 +50,7 @@ func is_valid() -> bool: func copy() -> PlayerSettings: var result := PlayerSettings.new() result.auto_click_enabled = auto_click_enabled + result.use_readable_interface_font = use_readable_interface_font result.auto_click_interval = auto_click_interval result.mouse_camera_sensitivity = mouse_camera_sensitivity result.controller_camera_sensitivity = controller_camera_sensitivity diff --git a/settings/player_settings_manager.gd b/settings/player_settings_manager.gd index 7e989a7..3cf737e 100644 --- a/settings/player_settings_manager.gd +++ b/settings/player_settings_manager.gd @@ -45,10 +45,17 @@ func load_settings() -> bool: if ( typeof(accessibility.get("auto_click_enabled")) != TYPE_BOOL or typeof(camera.get("invert_vertical")) != TYPE_BOOL + or ( + accessibility.has("use_readable_interface_font") + and typeof(accessibility["use_readable_interface_font"]) != TYPE_BOOL + ) ): return _use_defaults_after_corruption("Player settings values are invalid.") var loaded := PlayerSettings.new() loaded.auto_click_enabled = accessibility["auto_click_enabled"] + loaded.use_readable_interface_font = bool( + accessibility.get("use_readable_interface_font", false) + ) loaded.auto_click_interval = _read_float( accessibility.get("auto_click_interval"), -1.0 @@ -124,6 +131,9 @@ func save_now() -> bool: "accessibility": { "auto_click_enabled": current_settings.auto_click_enabled, "auto_click_interval": current_settings.auto_click_interval, + "use_readable_interface_font": ( + current_settings.use_readable_interface_font + ), }, "camera": { "mouse_sensitivity": current_settings.mouse_camera_sensitivity, diff --git a/ui/fonts/Tuffy-LICENSE.txt b/ui/fonts/Tuffy-LICENSE.txt new file mode 100644 index 0000000..defced0 --- /dev/null +++ b/ui/fonts/Tuffy-LICENSE.txt @@ -0,0 +1,11 @@ +We, the copyright holders of this work, hereby release it into the +public domain. This applies worldwide. + +In case this is not legally possible, + +We grant any entity the right to use this work for any purpose, without +any conditions, unless such conditions are required by law. + +Thatcher Ulrich http://tulrich.com +Karoly Barta bartakarcsi@gmail.com +Michael Evans http://www.evertype.com diff --git a/ui/fonts/Tuffy_Bold.otf b/ui/fonts/Tuffy_Bold.otf new file mode 100644 index 0000000..805ef2e Binary files /dev/null and b/ui/fonts/Tuffy_Bold.otf differ diff --git a/ui/fonts/Tuffy_Bold.otf.import b/ui/fonts/Tuffy_Bold.otf.import new file mode 100644 index 0000000..04a768a --- /dev/null +++ b/ui/fonts/Tuffy_Bold.otf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://xhdms1j01k2q" +path="res://.godot/imported/Tuffy_Bold.otf-1ed903e9af3906035b0cf09ea141ce25.fontdata" + +[deps] + +source_file="res://ui/fonts/Tuffy_Bold.otf" +dest_files=["res://.godot/imported/Tuffy_Bold.otf-1ed903e9af3906035b0cf09ea141ce25.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=3 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/ui/game_ui.gd b/ui/game_ui.gd index 4f3ec51..acd54c4 100644 --- a/ui/game_ui.gd +++ b/ui/game_ui.gd @@ -184,6 +184,7 @@ func setup_data_and_identity( player_identity: PlayerIdentityStore, host_identity: HostIdentityStore, network_session: NetworkSession, + interface_fonts: InterfaceFontController, ) -> void: for panel: SettingsPanelType in [ _title_settings_panel, _pause_settings_panel @@ -194,6 +195,7 @@ func setup_data_and_identity( player_identity, host_identity, network_session, + interface_fonts, ) @@ -219,7 +221,7 @@ func _process(_delta: float) -> void: }.get(item_id, "") parts.append( "%s %d:%02d" - % [label, int(remaining) / 60, int(remaining) % 60] + % [label, floori(remaining / 60.0), int(remaining) % 60] ) _effect_status.text = " ".join(parts) _effect_status.visible = not parts.is_empty() diff --git a/ui/interface_font_controller.gd b/ui/interface_font_controller.gd new file mode 100644 index 0000000..88562e0 --- /dev/null +++ b/ui/interface_font_controller.gd @@ -0,0 +1,55 @@ +class_name InterfaceFontController +extends Node + +signal font_mode_changed(use_readable_font: bool) + +const READABLE_FONT_PATH: String = "res://ui/fonts/Tuffy_Bold.otf" + +var _game_theme: Theme = preload("res://ui/game_theme.tres") +var _game_font: Font = preload("res://ui/fonts/seattle_avenue.otf") +var _readable_font: Font +var _utility_theme: Theme +var _use_readable_font: bool = false + + +func _ready() -> void: + if ResourceLoader.exists(READABLE_FONT_PATH): + _readable_font = load(READABLE_FONT_PATH) as Font + if _readable_font == null: + push_error("Readable interface font resource is unavailable.") + else: + _readable_font.fallbacks = [_game_font] + _utility_theme = _game_theme.duplicate(true) + _utility_theme.default_font = readable_font() + + +func set_readable_font_enabled(enabled: bool) -> void: + _use_readable_font = enabled + _game_theme.default_font = ( + _readable_font + if enabled and _readable_font != null + else _game_font + ) + font_mode_changed.emit(_use_readable_font) + + +func is_readable_font_enabled() -> bool: + return _use_readable_font + + +func apply_utility_theme(themed_node: Node) -> void: + if themed_node == null: + return + if _utility_theme == null: + _utility_theme = _game_theme.duplicate(true) + _utility_theme.default_font = ( + _readable_font if _readable_font != null else _game_font + ) + if themed_node is Control: + (themed_node as Control).theme = _utility_theme + elif themed_node is Window: + (themed_node as Window).theme = _utility_theme + + +func readable_font() -> Font: + return _readable_font if _readable_font != null else _game_font diff --git a/ui/interface_font_controller.gd.uid b/ui/interface_font_controller.gd.uid new file mode 100644 index 0000000..f6b4897 --- /dev/null +++ b/ui/interface_font_controller.gd.uid @@ -0,0 +1 @@ +uid://djmxy6jibi5y3 diff --git a/ui/player_menu.gd b/ui/player_menu.gd index 943ac39..243a858 100644 --- a/ui/player_menu.gd +++ b/ui/player_menu.gd @@ -194,8 +194,6 @@ enum CloseReason { @onready var _selection_status: BubbleStatusBubbleType = %SelectionStatus @onready var _offer_status: BubbleStatusBubbleType = %OfferStatus @onready var _inventory_section: Control = %InventorySection -@onready var _inventory_body: BoxContainer = %InventoryBody -@onready var _inventory_scroll: ScrollContainer = %InventoryScroll @onready var _bag_section: Control = %BagSection @onready var _logbook_section: Control = %LogbookSection @onready var _bag_empty: Label = %BagEmpty @@ -210,9 +208,6 @@ enum CloseReason { @onready var _held_value: Label = %HeldValue @onready var _cooler_count: Label = %CoolerCount @onready var _inventory_empty: Label = %InventoryEmpty -@onready var _inventory_grid: GridContainer = %InventoryGrid -@onready var _inventory_list: Control = %InventoryList -@onready var _detail_panel: Control = %DetailPanel @onready var _detail_texture: TextureRect = %DetailTexture @onready var _detail_name: Label = %DetailName @onready var _detail_data: Label = %DetailData @@ -227,8 +222,6 @@ enum CloseReason { @onready var _cancel_sale_button: Button = %CancelSaleButton @onready var _logbook_empty: Label = %LogbookEmpty @onready var _logbook_grid: GridContainer = %LogbookGrid -@onready var _status_shoal: HBoxContainer = %StatusShoal -@onready var _sort_bar: HBoxContainer = %SortBar var _compact_layout: bool = false var _player: PlayerType @@ -1181,6 +1174,7 @@ func _update_shell_layout() -> void: _logbook_page.size = reference_size _logbook_page.position = Vector2.ZERO _logbook_rest_position = Vector2.ZERO + _mail_page.set_anchors_preset(Control.PRESET_TOP_LEFT) _mail_page.size = reference_size _mail_page.position = Vector2.ZERO _mail_rest_position = Vector2.ZERO @@ -1732,7 +1726,10 @@ func _reset_page_transition_visuals() -> void: func _on_sort_selected(index: int, source: OptionButton) -> void: - _sort_mode = source.get_item_id(index) + var selected_id: int = source.get_item_id(index) + if selected_id < SortMode.CATCH_ORDER or selected_id > SortMode.RARITY: + return + _sort_mode = selected_id as SortMode _sort_option.select(_sort_mode) _cooler_sort_option.select(_sort_mode) _refresh_inventory() @@ -1891,7 +1888,7 @@ func _layout_bag_items() -> void: var row: int = floori(float(index) / float(columns)) var stable_offset := Vector2( float(identity_hash % 13) - 6.0, - float((identity_hash / 19) % 11) - 5.0, + float(floori(float(identity_hash) / 19.0) % 11) - 5.0, ) var lane_offset: float = 18.0 if row % 2 == 1 else 0.0 item_node.custom_minimum_size = item_size @@ -2225,7 +2222,7 @@ func _layout_cooler_fish(animate: bool = true) -> void: var offset_center: float = 2.0 if _compact_layout else 3.0 var stable_offset := Vector2( float(identity_hash % offset_span) - offset_center, - float((identity_hash / 17) % offset_span) - offset_center, + float(floori(float(identity_hash) / 17.0) % offset_span) - offset_center, ) var lane_offset: float = 5.0 if row % 2 == 1 else 0.0 var target := Vector2( diff --git a/ui/settings_panel.gd b/ui/settings_panel.gd index e7a6875..830f2b4 100644 --- a/ui/settings_panel.gd +++ b/ui/settings_panel.gd @@ -48,6 +48,7 @@ enum PresentationMode { @onready var _invert_y_toggle: BubbleButton = %InvertYToggle @onready var _auto_click_toggle: BubbleButton = %AutoClickToggle @onready var _auto_click_interval: BubbleButton = %AutoClickIntervalValue +@onready var _readable_font_toggle: BubbleButton = %ReadableFontToggle @onready var _world_options: Array[BubbleButton] = [ %WorldLegible, @@ -83,6 +84,7 @@ var _data_root: PlayerDataRoot var _identity_backups: IdentityBackupService var _player_identity: PlayerIdentityStore var _host_identity: HostIdentityStore +var _interface_fonts: InterfaceFontController var _data_folder_dialog: FileDialog var _backup_file_dialog: FileDialog var _export_file_dialog: FileDialog @@ -148,6 +150,7 @@ func _ready() -> void: ) _invert_y_toggle.pressed.connect(_toggle_invert_y) _auto_click_toggle.pressed.connect(_toggle_auto_click) + _readable_font_toggle.pressed.connect(_toggle_readable_font) %IntervalDecrease.pressed.connect(_adjust_auto_click_interval.bind(-1)) %IntervalIncrease.pressed.connect(_adjust_auto_click_interval.bind(1)) _auto_click_interval.pressed.connect( @@ -185,13 +188,16 @@ func setup_data_and_identity( player_identity: PlayerIdentityStore, host_identity: HostIdentityStore, session: NetworkSession, + interface_fonts: InterfaceFontController, ) -> void: _data_root = data_root _identity_backups = identity_backups _player_identity = player_identity _host_identity = host_identity _network_session = session + _interface_fonts = interface_fonts _identity_backups.operation_finished.connect(_on_identity_operation_finished) + _interface_fonts.apply_utility_theme(_data_page) _refresh_data_page() @@ -410,14 +416,18 @@ func _choose_data_folder() -> void: _data_folder_dialog = FileDialog.new() _data_folder_dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR _data_folder_dialog.access = FileDialog.ACCESS_FILESYSTEM + _data_folder_dialog.use_native_dialog = false _data_folder_dialog.dir_selected.connect(_change_data_folder) + _interface_fonts.apply_utility_theme(_data_folder_dialog) add_child(_data_folder_dialog) _data_folder_dialog.current_dir = _data_root.root_path.get_base_dir() _data_folder_dialog.popup_centered_ratio(0.75) func _change_data_folder(path: String) -> void: - var result := PortableDataMigration.migrate_active_to(_data_root, path) + var result: Dictionary = PortableDataMigration.migrate_active_to( + _data_root, path + ) if bool(result.get("requires_existing_root_decision", false)): _show_existing_data_folder_choice(path) return @@ -430,7 +440,7 @@ func _change_data_folder(path: String) -> void: func _show_existing_data_folder_choice(path: String) -> void: - var dialog := ConfirmationDialog.new() + var dialog: ConfirmationDialog = ConfirmationDialog.new() dialog.title = "Existing NETFISHING data" dialog.ok_button_text = "Use Selected Data" dialog.dialog_text = ( @@ -449,7 +459,7 @@ func _show_existing_data_folder_choice(path: String) -> void: dialog.custom_action.connect(func(action: StringName) -> void: if action != &"replace": return - var result := PortableDataMigration.replace_existing_with_active( + var result: Dictionary = PortableDataMigration.replace_existing_with_active( _data_root, path ) _feedback.text = str(result.get("message", "Could not replace selected data.")) @@ -457,6 +467,7 @@ func _show_existing_data_folder_choice(path: String) -> void: get_tree().call_deferred("quit") dialog.queue_free() ) + _interface_fonts.apply_utility_theme(dialog) add_child(dialog) dialog.popup_centered(Vector2i(620, 340)) @@ -466,13 +477,15 @@ func _choose_identity_export(identity_type: String) -> void: return _pending_identity_operation = "export" _pending_identity_type = identity_type - var suggested := _identity_backups.default_export_path(identity_type) + var suggested: String = _identity_backups.default_export_path(identity_type) if _export_file_dialog == null: _export_file_dialog = FileDialog.new() _export_file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE _export_file_dialog.access = FileDialog.ACCESS_FILESYSTEM + _export_file_dialog.use_native_dialog = false _export_file_dialog.filters = PackedStringArray(["*.nfidentity ; NETFISHING identity backup"]) _export_file_dialog.file_selected.connect(_identity_export_file_selected) + _interface_fonts.apply_utility_theme(_export_file_dialog) add_child(_export_file_dialog) _export_file_dialog.current_dir = suggested.get_base_dir() _export_file_dialog.current_file = suggested.get_file() @@ -495,8 +508,10 @@ func _choose_identity_import(identity_type: String) -> void: _backup_file_dialog = FileDialog.new() _backup_file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE _backup_file_dialog.access = FileDialog.ACCESS_FILESYSTEM + _backup_file_dialog.use_native_dialog = false _backup_file_dialog.filters = PackedStringArray(["*.nfidentity ; NETFISHING identity backup"]) _backup_file_dialog.file_selected.connect(_identity_import_file_selected) + _interface_fonts.apply_utility_theme(_backup_file_dialog) add_child(_backup_file_dialog) _backup_file_dialog.current_dir = _data_root.identity_backup_directory() _backup_file_dialog.popup_centered_ratio(0.75) @@ -512,8 +527,8 @@ func _show_passphrase_dialog(exporting: bool) -> void: _passphrase_dialog = ConfirmationDialog.new() _passphrase_dialog.title = "Encrypted identity backup" _passphrase_dialog.confirmed.connect(_submit_identity_passphrase) - var fields := VBoxContainer.new() - var warning := Label.new() + var fields: VBoxContainer = VBoxContainer.new() + var warning: Label = Label.new() warning.text = ( "Anyone with this backup and passphrase can use your identity." ) @@ -528,6 +543,7 @@ func _show_passphrase_dialog(exporting: bool) -> void: _passphrase_confirm.secret = true fields.add_child(_passphrase_confirm) _passphrase_dialog.add_child(fields) + _interface_fonts.apply_utility_theme(_passphrase_dialog) add_child(_passphrase_dialog) _passphrase_confirm.visible = exporting _passphrase_entry.clear() @@ -541,7 +557,7 @@ func _show_passphrase_dialog(exporting: bool) -> void: func _submit_identity_passphrase() -> void: - var passphrase := _passphrase_entry.text + var passphrase: String = _passphrase_entry.text if _pending_identity_operation == "export": _identity_backups.export_backup( _pending_identity_type, @@ -550,7 +566,7 @@ func _submit_identity_passphrase() -> void: _passphrase_confirm.text, ) return - var inspected := _identity_backups.import_backup( + var inspected: Dictionary = _identity_backups.import_backup( _pending_identity_type, _pending_identity_path, passphrase, @@ -566,7 +582,7 @@ func _submit_identity_passphrase() -> void: func _show_identity_replacement_confirmation() -> void: - var dialog := ConfirmationDialog.new() + var dialog: ConfirmationDialog = ConfirmationDialog.new() dialog.title = "Replace active identity?" dialog.ok_button_text = "Review Replacement" dialog.dialog_text = ( @@ -579,6 +595,7 @@ func _show_identity_replacement_confirmation() -> void: dialog.set_meta("confirmation_step", 1) dialog.confirmed.connect(_advance_identity_replacement.bind(dialog)) dialog.canceled.connect(dialog.queue_free) + _interface_fonts.apply_utility_theme(dialog) add_child(dialog) dialog.popup_centered(Vector2i(620, 360)) @@ -659,6 +676,9 @@ func _load_controls() -> void: _mouse_sensitivity = settings.mouse_camera_sensitivity _controller_sensitivity = settings.controller_camera_sensitivity _invert_camera_y = settings.invert_camera_y + _readable_font_toggle.button_pressed = ( + settings.use_readable_interface_font + ) _refresh_value_labels() @@ -686,6 +706,14 @@ func _refresh_value_labels() -> void: "accessibility\nauto-click\n" + ("on" if _auto_click_enabled else "off") ) + _readable_font_toggle.text = ( + "readable\ninterface font\n" + + ( + "on" + if settings.use_readable_interface_font + else "off" + ) + ) _auto_click_interval.text = ( "auto-click\ninterval\n%.2f s" % _auto_click_interval_value ) @@ -709,6 +737,19 @@ func _set_world_pixelation(pixel_size: int) -> void: _refresh_value_labels() +func _toggle_readable_font() -> void: + if _settings_manager == null: + return + var edited: PlayerSettings = _settings_manager.current_settings.copy() + edited.use_readable_interface_font = ( + not edited.use_readable_interface_font + ) + if not _settings_manager.apply_settings(edited): + _feedback.text = "failed to save accessibility settings." + return + _load_controls() + + func _set_ui_pixelation(pixel_size: int) -> void: if _settings_manager == null: return diff --git a/ui/settings_panel.tscn b/ui/settings_panel.tscn index 3055d3f..f41cba8 100644 --- a/ui/settings_panel.tscn +++ b/ui/settings_panel.tscn @@ -486,8 +486,8 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("3_page") page_id = &"accessibility" -bubble_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/IntervalHelp"), NodePath("BubbleCluster/AccessibilityBackButton")]) -focus_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/AccessibilityBackButton")]) +bubble_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/ReadableFontToggle"), NodePath("BubbleCluster/IntervalHelp"), NodePath("BubbleCluster/AccessibilityBackButton")]) +focus_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/ReadableFontToggle"), NodePath("BubbleCluster/AccessibilityBackButton")]) initial_focus_path = NodePath("BubbleCluster/AutoClickToggle") back_focus_path = NodePath("BubbleCluster/AccessibilityBackButton") @@ -562,6 +562,41 @@ minimum_font_size = 13 maximum_font_size = 22 motion_phase = 4.1 +[node name="ReadableFontToggle" parent="AccessibilityPage/BubbleCluster" instance=ExtResource("4_bubble")] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 0) +toggle_mode = true +text = "readable\ninterface font\noff" +neutral_size = Vector2(180, 132) +desktop_anchor = Vector2(92, 405) +compact_anchor = Vector2(102, 374) +compact_minimum_size = Vector2(156, 118) +minimum_font_size = 13 +maximum_font_size = 21 +motion_phase = 4.6 + +[node name="ReadableFontSample" type="Label" parent="AccessibilityPage/BubbleCluster"] +layout_mode = 0 +offset_left = 250.0 +offset_top = 20.0 +offset_right = 700.0 +offset_bottom = 82.0 +mouse_filter = 2 +text = "The quick brown fox jumps over the lazy dog.\n0123456789" +horizontal_alignment = 1 +vertical_alignment = 1 +autowrap_mode = 2 + +[node name="ReadableFontHelper" type="Label" parent="AccessibilityPage/BubbleCluster"] +layout_mode = 0 +offset_left = 250.0 +offset_top = 82.0 +offset_right = 700.0 +offset_bottom = 112.0 +mouse_filter = 2 +text = "Uses a clearer font for menus and interface text." +horizontal_alignment = 1 + [node name="AccessibilityBackButton" parent="AccessibilityPage/BubbleCluster" instance=ExtResource("4_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0)