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:
Alexander Sellite 2026-08-23 20:48:38 -04:00
parent 1db1a5b754
commit 3b84bfe3a0
97 changed files with 7869 additions and 982 deletions

View file

@ -81,12 +81,36 @@ func _run() -> void:
"%NetworkSurfaceDrawingService"
) as NetworkSurfaceDrawingService
var game_ui := main.get_node("%GameUI") as GameUI
var keyboard_mapping := main.get_node(
"%KeyboardMouseMappingManager"
) as KeyboardMouseMappingManager
var toolbar := game_ui.get_node(
"%SurfaceDrawingToolbar"
) as SurfaceDrawingToolbar
var chat_ui := game_ui.get_node("%ChatUI") as ChatUI
assert(player != null and service != null and toolbar != null)
assert(chat_ui != null)
assert(keyboard_mapping != null)
assert(keyboard_mapping.reset_mapping())
await process_frame
var shop_prompt_key := game_ui.get_node("%ShopPromptKey") as Label
var storage_prompt_message := game_ui.get_node(
"%StoragePromptMessage"
) as Label
assert(shop_prompt_key.text == "E")
assert(storage_prompt_message.text == "E open storage")
var remapped_interact := InputEventKey.new()
remapped_interact.physical_keycode = KEY_F
remapped_interact.pressed = true
assert(keyboard_mapping.set_binding(
KeyboardMouseMappingManager.ROLE_INTERACT,
keyboard_mapping.binding_from_event(remapped_interact),
))
await process_frame
assert(shop_prompt_key.text == "F")
assert(storage_prompt_message.text == "F open storage")
assert(keyboard_mapping.reset_mapping())
await process_frame
assert(toolbar.get_parent() == chat_ui.get_parent())
assert(
toolbar.get_index() > chat_ui.get_index(),
@ -252,6 +276,33 @@ func _run() -> void:
else:
assert(not typed_chat_entry.virtual_keyboard_enabled)
assert(on_screen_keyboard.is_open())
# A bite temporarily owns gameplay input without discarding an in-progress
# message. The draft and insertion point return only after fishing releases
# that ownership, and any controller keyboard follows the restored field.
var chat_fishing_spot := main.get_node("%FishingSpot") as FishingSpot
assert(chat_fishing_spot != null)
var interrupted_draft := "finish this message after fishing"
var interrupted_caret: int = 12
typed_chat_entry.text = interrupted_draft
typed_chat_entry.caret_column = interrupted_caret
chat_fishing_spot.call("_set_fishing_input_priority", true)
await process_frame
assert(not chat_ui.is_open())
assert(not bool(chat_ui.get("_input_lock_applied")))
assert(chat_ui.has_fishing_resume_pending())
assert(not on_screen_keyboard.is_open())
chat_ui.open_chat()
assert(not chat_ui.is_open())
chat_fishing_spot.call("_set_fishing_input_priority", false)
for _frame: int in 4:
await process_frame
assert(chat_ui.is_open())
assert(typed_chat_entry.text == interrupted_draft)
assert(typed_chat_entry.caret_column == interrupted_caret)
assert(bool(chat_ui.get("_input_lock_applied")))
if not DisplayServer.has_feature(DisplayServer.FEATURE_VIRTUAL_KEYBOARD):
assert(on_screen_keyboard.is_open())
on_screen_keyboard.call("_close_keyboard", true)
assert(typed_chat_entry.has_focus())
var left_bumper := InputEventJoypadButton.new()
@ -507,7 +558,7 @@ func _run() -> void:
and stamp_button != null
)
assert(export_button.text == "png")
assert(export_button.tooltip_text == "export aimed artwork as PNG")
assert(export_button.tooltip_text == "aim at artwork, then click to export PNG")
assert(stamp_button.text == "stamp")
assert(stamp_button.disabled)
assert(toolbar.get_node_or_null("%CloseButton") == null)
@ -830,6 +881,18 @@ func _run() -> void:
await process_frame
assert(player.hotbar.get_selected_slot() == 1)
assert(not service.is_active() and not toolbar.visible)
hotbar_ui.set_swap_hotbar_camera_scroll(true)
hotbar_ui._unhandled_input(wheel_down)
await process_frame
assert(player.hotbar.get_selected_slot() == 1)
var shifted_wheel_down := InputEventMouseButton.new()
shifted_wheel_down.button_index = MOUSE_BUTTON_WHEEL_DOWN
shifted_wheel_down.pressed = true
shifted_wheel_down.shift_pressed = true
hotbar_ui._unhandled_input(shifted_wheel_down)
await process_frame
assert(player.hotbar.get_selected_slot() == 2)
hotbar_ui.set_swap_hotbar_camera_scroll(false)
var number_one := InputEventKey.new()
number_one.physical_keycode = KEY_1