feat: expand progression and multiplayer systems
Add named save slots, progression import/export, and a unified play flow. Add live friend requests, presence, invitations, and relationship controls without durable discovery-server social storage. Advance the network protocol with isolated channels, movement reconciliation, late-join recovery, fishing replication, and animation synchronization. Preserve per-species catch totals, refine generated-world startup and water recovery, and complete the related input and interface improvements.
This commit is contained in:
parent
1db1a5b754
commit
3b84bfe3a0
97 changed files with 7869 additions and 982 deletions
|
|
@ -86,6 +86,7 @@ enum PresentationMode {
|
|||
%ControllerSensitivityValue
|
||||
)
|
||||
@onready var _invert_y_toggle: Button = %InvertYToggle
|
||||
@onready var _swap_scroll_toggle: Button = %SwapScrollToggle
|
||||
@onready var _on_screen_keyboard_toggle: Button = %OnScreenKeyboardToggle
|
||||
@onready var _auto_click_toggle: Button = %AutoClickToggle
|
||||
@onready var _auto_click_interval_slider: HSlider = (
|
||||
|
|
@ -112,6 +113,7 @@ var _auto_click_interval_value: float = 0.20
|
|||
var _mouse_sensitivity: float = 0.005
|
||||
var _controller_sensitivity: float = 2.5
|
||||
var _invert_camera_y: bool = false
|
||||
var _swap_hotbar_camera_scroll: bool = false
|
||||
var _on_screen_keyboard_enabled: bool = false
|
||||
var _chat_dock_right: bool = false
|
||||
var _chat_mobile_mode: bool = false
|
||||
|
|
@ -125,7 +127,6 @@ var _environment_volume: float = 1.0
|
|||
var _network_profile: NetworkProfilePreferences
|
||||
var _network_session: NetworkSession
|
||||
var _data_root: PlayerDataRoot
|
||||
var _progression_saves: PlayerSaveManager
|
||||
var _identity_backups: IdentityBackupService
|
||||
var _player_identity: PlayerIdentityStore
|
||||
var _host_identity: HostIdentityStore
|
||||
|
|
@ -135,8 +136,6 @@ var _controller_mapping_panel: ControllerMappingPanelType
|
|||
var _keyboard_mouse_mapping_manager: KeyboardMouseMappingManagerType
|
||||
var _keyboard_mouse_mapping_panel: KeyboardMouseMappingPanelType
|
||||
var _data_folder_dialog: FileDialog
|
||||
var _progression_import_dialog: FileDialog
|
||||
var _progression_export_dialog: FileDialog
|
||||
var _backup_file_dialog: FileDialog
|
||||
var _export_file_dialog: FileDialog
|
||||
var _passphrase_dialog: ConfirmationDialog
|
||||
|
|
@ -146,7 +145,6 @@ var _pending_identity_operation := ""
|
|||
var _pending_identity_type := ""
|
||||
var _pending_identity_path := ""
|
||||
var _pending_import_data: Dictionary = {}
|
||||
var _pending_progression_path := ""
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
|
|
@ -239,6 +237,7 @@ func _connect_controls() -> void:
|
|||
_on_controller_sensitivity_changed
|
||||
)
|
||||
_invert_y_toggle.toggled.connect(_set_invert_y)
|
||||
_swap_scroll_toggle.toggled.connect(_set_swap_scroll)
|
||||
_on_screen_keyboard_toggle.toggled.connect(_set_on_screen_keyboard)
|
||||
_auto_click_toggle.toggled.connect(_set_auto_click)
|
||||
_auto_click_interval_slider.value_changed.connect(
|
||||
|
|
@ -248,8 +247,6 @@ func _connect_controls() -> void:
|
|||
%KeyboardMapping.pressed.connect(_open_keyboard_mouse_mapping)
|
||||
%OpenDataFolder.pressed.connect(_open_data_folder)
|
||||
%ChangeDataFolder.pressed.connect(_choose_data_folder)
|
||||
%ExportProgression.pressed.connect(_choose_progression_export)
|
||||
%ImportProgression.pressed.connect(_choose_progression_import)
|
||||
%ExportPlayerIdentity.pressed.connect(
|
||||
_choose_identity_export.bind("player")
|
||||
)
|
||||
|
|
@ -343,7 +340,6 @@ func setup_keyboard_mouse_mapping(
|
|||
|
||||
func setup_data_and_identity(
|
||||
data_root: PlayerDataRoot,
|
||||
progression_saves: PlayerSaveManager,
|
||||
identity_backups: IdentityBackupService,
|
||||
player_identity: PlayerIdentityStore,
|
||||
host_identity: HostIdentityStore,
|
||||
|
|
@ -351,7 +347,6 @@ func setup_data_and_identity(
|
|||
interface_fonts: InterfaceFontController,
|
||||
) -> void:
|
||||
_data_root = data_root
|
||||
_progression_saves = progression_saves
|
||||
_identity_backups = identity_backups
|
||||
_player_identity = player_identity
|
||||
_host_identity = host_identity
|
||||
|
|
@ -619,11 +614,6 @@ func _refresh_data_page() -> void:
|
|||
_data_root.override_active
|
||||
or (_network_session != null and _network_session.is_session_active())
|
||||
)
|
||||
%ExportProgression.disabled = _progression_saves == null
|
||||
%ImportProgression.disabled = (
|
||||
_progression_saves == null
|
||||
or (_network_session != null and _network_session.is_session_active())
|
||||
)
|
||||
var fingerprint: String = (
|
||||
_player_identity.fingerprint if _player_identity != null else ""
|
||||
)
|
||||
|
|
@ -674,122 +664,6 @@ func _copy_player_fingerprint() -> void:
|
|||
_feedback.text = "full player fingerprint copied for server operator setup."
|
||||
|
||||
|
||||
func _choose_progression_export() -> void:
|
||||
if _progression_saves == null or _data_root == null:
|
||||
_feedback.text = "progression export is unavailable."
|
||||
return
|
||||
var timestamp: String = Time.get_datetime_string_from_system().replace(
|
||||
":", "-"
|
||||
)
|
||||
var suggested: String = _data_root.progression_backup_directory().path_join(
|
||||
"NETfishing-progression-%s%s"
|
||||
% [timestamp, PlayerSaveManager.ARCHIVE_EXTENSION]
|
||||
)
|
||||
if _progression_export_dialog == null:
|
||||
_progression_export_dialog = FileDialog.new()
|
||||
_progression_export_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
|
||||
_progression_export_dialog.access = FileDialog.ACCESS_FILESYSTEM
|
||||
_progression_export_dialog.use_native_dialog = false
|
||||
_progression_export_dialog.filters = PackedStringArray([
|
||||
"*.nfsave ; NETfishing progression archive",
|
||||
])
|
||||
_progression_export_dialog.file_selected.connect(
|
||||
_progression_export_file_selected
|
||||
)
|
||||
_interface_fonts.apply_utility_theme(_progression_export_dialog)
|
||||
add_child(_progression_export_dialog)
|
||||
_progression_export_dialog.current_dir = suggested.get_base_dir()
|
||||
_progression_export_dialog.current_file = suggested.get_file()
|
||||
_interface_fonts.popup_file_dialog(_progression_export_dialog)
|
||||
|
||||
|
||||
func _progression_export_file_selected(path: String) -> void:
|
||||
var destination: String = (
|
||||
path
|
||||
if path.ends_with(PlayerSaveManager.ARCHIVE_EXTENSION)
|
||||
else path + PlayerSaveManager.ARCHIVE_EXTENSION
|
||||
)
|
||||
var result: Dictionary = _progression_saves.export_progression_archive(
|
||||
destination
|
||||
)
|
||||
_feedback.text = str(
|
||||
result.get("message", "progression export failed.")
|
||||
)
|
||||
|
||||
|
||||
func _choose_progression_import() -> void:
|
||||
if _progression_saves == null or _data_root == null:
|
||||
_feedback.text = "progression import is unavailable."
|
||||
return
|
||||
if _network_session != null and _network_session.is_session_active():
|
||||
_feedback.text = "return to title before importing progression."
|
||||
return
|
||||
if _progression_import_dialog == null:
|
||||
_progression_import_dialog = FileDialog.new()
|
||||
_progression_import_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
|
||||
_progression_import_dialog.access = FileDialog.ACCESS_FILESYSTEM
|
||||
_progression_import_dialog.use_native_dialog = false
|
||||
_progression_import_dialog.filters = PackedStringArray([
|
||||
"*.nfsave ; NETfishing progression archive",
|
||||
])
|
||||
_progression_import_dialog.file_selected.connect(
|
||||
_progression_import_file_selected
|
||||
)
|
||||
_interface_fonts.apply_utility_theme(_progression_import_dialog)
|
||||
add_child(_progression_import_dialog)
|
||||
_progression_import_dialog.current_dir = (
|
||||
_data_root.progression_backup_directory()
|
||||
)
|
||||
_interface_fonts.popup_file_dialog(_progression_import_dialog)
|
||||
|
||||
|
||||
func _progression_import_file_selected(path: String) -> void:
|
||||
var inspected: Dictionary = (
|
||||
_progression_saves.inspect_progression_archive(path)
|
||||
)
|
||||
if not bool(inspected.get("ok", false)):
|
||||
_feedback.text = str(
|
||||
inspected.get("message", "progression archive could not be opened.")
|
||||
)
|
||||
return
|
||||
_pending_progression_path = path
|
||||
var dialog := ConfirmationDialog.new()
|
||||
dialog.title = "replace saved progression?"
|
||||
dialog.ok_button_text = "import progression"
|
||||
dialog.dialog_text = (
|
||||
"this will replace the current progression after making a backup.\n\n"
|
||||
+ "fish: %d\ndiscovered: %d\nworld: %s\nworld seed: %d\n\n"
|
||||
+ "identities, settings, friends, bans, and trusted servers are unchanged."
|
||||
) % [
|
||||
int(inspected.get("catch_count", 0)),
|
||||
int(inspected.get("discovered_species_count", 0)),
|
||||
WorldLayout.display_name(inspected.get(
|
||||
"world_layout",
|
||||
String(WorldLayout.GENERATED),
|
||||
)),
|
||||
int(inspected.get("world_seed", 0)),
|
||||
]
|
||||
dialog.confirmed.connect(_confirm_progression_import.bind(dialog))
|
||||
dialog.canceled.connect(dialog.queue_free)
|
||||
_interface_fonts.apply_utility_theme(dialog)
|
||||
add_child(dialog)
|
||||
dialog.popup_centered(Vector2i(640, 390))
|
||||
_configure_confirmation_dialog.call_deferred(
|
||||
dialog, dialog.get_cancel_button()
|
||||
)
|
||||
|
||||
|
||||
func _confirm_progression_import(dialog: ConfirmationDialog) -> void:
|
||||
var result: Dictionary = _progression_saves.import_progression_archive(
|
||||
_pending_progression_path
|
||||
)
|
||||
_feedback.text = str(
|
||||
result.get("message", "progression import failed.")
|
||||
)
|
||||
_pending_progression_path = ""
|
||||
dialog.queue_free()
|
||||
|
||||
|
||||
func _choose_data_folder() -> void:
|
||||
if _data_root == null or _data_root.override_active:
|
||||
_feedback.text = "the data folder is externally managed."
|
||||
|
|
@ -1067,6 +941,7 @@ func _apply_settings() -> void:
|
|||
edited.mouse_camera_sensitivity = _mouse_sensitivity
|
||||
edited.controller_camera_sensitivity = _controller_sensitivity
|
||||
edited.invert_camera_y = _invert_camera_y
|
||||
edited.swap_hotbar_camera_scroll = _swap_hotbar_camera_scroll
|
||||
edited.on_screen_keyboard_enabled = _on_screen_keyboard_enabled
|
||||
edited.chat_dock_right = _chat_dock_right
|
||||
edited.chat_mobile_mode = _chat_mobile_mode
|
||||
|
|
@ -1099,6 +974,7 @@ func _load_controls() -> void:
|
|||
_mouse_sensitivity = settings.mouse_camera_sensitivity
|
||||
_controller_sensitivity = settings.controller_camera_sensitivity
|
||||
_invert_camera_y = settings.invert_camera_y
|
||||
_swap_hotbar_camera_scroll = settings.swap_hotbar_camera_scroll
|
||||
_on_screen_keyboard_enabled = settings.on_screen_keyboard_enabled
|
||||
_chat_dock_right = settings.chat_dock_right
|
||||
_chat_mobile_mode = settings.chat_mobile_mode
|
||||
|
|
@ -1125,6 +1001,7 @@ func _load_controls() -> void:
|
|||
_controller_sensitivity
|
||||
)
|
||||
_invert_y_toggle.set_pressed_no_signal(_invert_camera_y)
|
||||
_swap_scroll_toggle.set_pressed_no_signal(_swap_hotbar_camera_scroll)
|
||||
_on_screen_keyboard_toggle.set_pressed_no_signal(
|
||||
_on_screen_keyboard_enabled
|
||||
)
|
||||
|
|
@ -1142,6 +1019,9 @@ func _refresh_value_labels() -> void:
|
|||
"on" if _fullscreen_toggle.button_pressed else "off"
|
||||
)
|
||||
_invert_y_toggle.text = "on" if _invert_camera_y else "off"
|
||||
_swap_scroll_toggle.text = (
|
||||
"on" if _swap_hotbar_camera_scroll else "off"
|
||||
)
|
||||
_on_screen_keyboard_toggle.text = (
|
||||
"on" if _on_screen_keyboard_enabled else "off"
|
||||
)
|
||||
|
|
@ -1255,6 +1135,11 @@ func _set_invert_y(enabled: bool) -> void:
|
|||
_refresh_value_labels()
|
||||
|
||||
|
||||
func _set_swap_scroll(enabled: bool) -> void:
|
||||
_swap_hotbar_camera_scroll = enabled
|
||||
_refresh_value_labels()
|
||||
|
||||
|
||||
func _set_on_screen_keyboard(enabled: bool) -> void:
|
||||
_on_screen_keyboard_enabled = enabled
|
||||
_refresh_value_labels()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue