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
|
|
@ -7,6 +7,7 @@ signal back_requested
|
|||
|
||||
enum Mode {
|
||||
DISCOVER,
|
||||
FRIENDS,
|
||||
DIRECT,
|
||||
SAVED,
|
||||
RECENT,
|
||||
|
|
@ -26,6 +27,12 @@ const DIRECT_WORKFLOW_HELP: String = (
|
|||
% ADDRESS_FORMAT_HELP
|
||||
)
|
||||
|
||||
@onready var _main_panel: PanelContainer = %MainPanel
|
||||
@onready var _content_panel: PanelContainer = %ContentPanel
|
||||
@onready var _direct_content: Control = %DirectContent
|
||||
@onready var _list_content: Control = %ListContent
|
||||
@onready var _list_title: Label = %ListTitle
|
||||
@onready var _details_panel: PanelContainer = %DetailsPanel
|
||||
@onready var _address: LineEdit = %Address
|
||||
@onready var _address_label: Label = %AddressLabel
|
||||
@onready var _address_helper: Label = %AddressHelper
|
||||
|
|
@ -34,10 +41,11 @@ const DIRECT_WORKFLOW_HELP: String = (
|
|||
@onready var _name_helper: Label = %NameHelper
|
||||
@onready var _server_list: ItemList = %ServerList
|
||||
@onready var _details: Label = %Details
|
||||
@onready var _discover_button: Button = %DiscoverButton
|
||||
@onready var _direct_button: Button = %DirectButton
|
||||
@onready var _saved_button: Button = %SavedButton
|
||||
@onready var _recent_button: Button = %RecentButton
|
||||
@onready var _discover_button: OrganizerTab = %DiscoverButton
|
||||
@onready var _friends_button: OrganizerTab = %FriendsButton
|
||||
@onready var _direct_button: OrganizerTab = %DirectButton
|
||||
@onready var _saved_button: OrganizerTab = %SavedButton
|
||||
@onready var _recent_button: OrganizerTab = %RecentButton
|
||||
@onready var _join_button: Button = %JoinButton
|
||||
@onready var _refresh_button: Button = %RefreshButton
|
||||
@onready var _save_button: Button = %SaveButton
|
||||
|
|
@ -62,6 +70,8 @@ var _visible_entries: Array[SavedServerEntry] = []
|
|||
var _selected_entry: SavedServerEntry
|
||||
var _discovery_rooms: Array[Dictionary] = []
|
||||
var _selected_discovery_index: int = -1
|
||||
var _friend_entries: Array[Dictionary] = []
|
||||
var _selected_friend_index: int = -1
|
||||
var _discovery_refresh_timer: Timer
|
||||
var _editing_entry_id: String = ""
|
||||
var _name_entry_active: bool = false
|
||||
|
|
@ -69,24 +79,13 @@ var _delete_armed: bool = false
|
|||
var _connection_error_latched: bool = false
|
||||
var _pending_confirmation_endpoint: String = ""
|
||||
var _pending_confirmation_room: Dictionary = {}
|
||||
var _owns_pending_public_join: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
UtilityPageStyle.apply_page(self)
|
||||
var paper := get_node("Paper") as PanelContainer
|
||||
paper.add_theme_stylebox_override(
|
||||
"panel", UtilityPageStyle.panel_style()
|
||||
)
|
||||
for button: BaseButton in [
|
||||
_discover_button, _direct_button, _saved_button, _recent_button,
|
||||
_refresh_button, _join_button,
|
||||
_save_button, _edit_button, _favorite_button, _delete_button,
|
||||
_cancel_button, _back_button,
|
||||
]:
|
||||
UtilityPageStyle.apply_ocean_button(button)
|
||||
UtilityPageStyle.apply_ocean_line_edit(_address)
|
||||
UtilityPageStyle.apply_ocean_line_edit(_name_edit)
|
||||
_configure_style()
|
||||
_discover_button.pressed.connect(_set_mode.bind(Mode.DISCOVER))
|
||||
_friends_button.pressed.connect(_set_mode.bind(Mode.FRIENDS))
|
||||
_direct_button.pressed.connect(_set_mode.bind(Mode.DIRECT))
|
||||
_saved_button.pressed.connect(_set_mode.bind(Mode.SAVED))
|
||||
_recent_button.pressed.connect(_set_mode.bind(Mode.RECENT))
|
||||
|
|
@ -121,6 +120,96 @@ func _ready() -> void:
|
|||
hide()
|
||||
|
||||
|
||||
func _configure_style() -> void:
|
||||
UtilityPageStyle.apply_page(self)
|
||||
_main_panel.add_theme_stylebox_override(
|
||||
"panel",
|
||||
UtilityPageStyle.rounded_style(
|
||||
UtilityPageStyle.OCEAN_PANEL_MID,
|
||||
28,
|
||||
),
|
||||
)
|
||||
_content_panel.add_theme_stylebox_override(
|
||||
"panel",
|
||||
UtilityPageStyle.rounded_style(
|
||||
UtilityPageStyle.OCEAN_FIELD,
|
||||
20,
|
||||
),
|
||||
)
|
||||
_details_panel.add_theme_stylebox_override(
|
||||
"panel",
|
||||
UtilityPageStyle.row_style(),
|
||||
)
|
||||
_server_list.add_theme_stylebox_override(
|
||||
"panel",
|
||||
UtilityPageStyle.rounded_style(
|
||||
UtilityPageStyle.OCEAN_PANEL_MID,
|
||||
12,
|
||||
),
|
||||
)
|
||||
_server_list.add_theme_stylebox_override(
|
||||
"focus",
|
||||
StyleBoxEmpty.new(),
|
||||
)
|
||||
_server_list.add_theme_stylebox_override(
|
||||
"selected",
|
||||
UtilityPageStyle.row_style(true),
|
||||
)
|
||||
_server_list.add_theme_stylebox_override(
|
||||
"selected_focus",
|
||||
UtilityPageStyle.row_style(true),
|
||||
)
|
||||
_server_list.add_theme_color_override(
|
||||
"font_color",
|
||||
UtilityPageStyle.OCEAN_TEXT_PRIMARY,
|
||||
)
|
||||
_server_list.add_theme_color_override(
|
||||
"font_selected_color",
|
||||
UtilityPageStyle.OCEAN_TEXT_PRIMARY,
|
||||
)
|
||||
for node: Node in find_children("*", "Label", true, false):
|
||||
var label := node as Label
|
||||
label.add_theme_color_override(
|
||||
"font_color",
|
||||
UtilityPageStyle.OCEAN_TEXT_PRIMARY,
|
||||
)
|
||||
for node: Node in find_children("*", "Button", true, false):
|
||||
if node is OrganizerTab:
|
||||
continue
|
||||
UtilityPageStyle.apply_ocean_button(node as BaseButton)
|
||||
UtilityPageStyle.apply_ocean_line_edit(_address)
|
||||
UtilityPageStyle.apply_ocean_line_edit(_name_edit)
|
||||
_join_button.add_theme_stylebox_override(
|
||||
"normal",
|
||||
UtilityPageStyle.ocean_button_style(
|
||||
UtilityPageStyle.GREEN,
|
||||
),
|
||||
)
|
||||
_delete_button.add_theme_stylebox_override(
|
||||
"normal",
|
||||
UtilityPageStyle.ocean_button_style(
|
||||
UtilityPageStyle.OCEAN_DANGER,
|
||||
),
|
||||
)
|
||||
_back_button.add_theme_stylebox_override(
|
||||
"normal",
|
||||
UtilityPageStyle.ocean_button_style(
|
||||
UtilityPageStyle.OCEAN_PANEL_DEEP,
|
||||
),
|
||||
)
|
||||
for secondary_label: Label in [
|
||||
_address_helper,
|
||||
_name_helper,
|
||||
_details,
|
||||
_status,
|
||||
_session_summary,
|
||||
]:
|
||||
secondary_label.add_theme_color_override(
|
||||
"font_color",
|
||||
UtilityPageStyle.OCEAN_TEXT_SECONDARY,
|
||||
)
|
||||
|
||||
|
||||
func setup(
|
||||
network_session: NetworkSession,
|
||||
saved_servers: SavedServerStore,
|
||||
|
|
@ -177,6 +266,19 @@ func setup(
|
|||
_discovery.public_join_status_changed.connect(
|
||||
_on_public_join_status_changed
|
||||
)
|
||||
if not _discovery.friend_presence_updated.is_connected(
|
||||
_on_friend_presence_updated
|
||||
):
|
||||
_discovery.friend_presence_updated.connect(
|
||||
_on_friend_presence_updated
|
||||
)
|
||||
if not _discovery.social_status_changed.is_connected(
|
||||
_on_social_status_changed
|
||||
):
|
||||
_discovery.social_status_changed.connect(
|
||||
_on_social_status_changed
|
||||
)
|
||||
_friend_entries = _discovery.get_friend_presence()
|
||||
_refresh()
|
||||
|
||||
|
||||
|
|
@ -232,7 +334,9 @@ func confirm_pending_join() -> bool:
|
|||
var room: Dictionary = _pending_confirmation_room.duplicate(true)
|
||||
cancel_pending_join_confirmation()
|
||||
if not room.is_empty():
|
||||
_owns_pending_public_join = true
|
||||
if _discovery == null or not _discovery.prepare_public_join(room):
|
||||
_owns_pending_public_join = false
|
||||
_set_status("Could not prepare the public connection.", true)
|
||||
return false
|
||||
return true
|
||||
|
|
@ -244,15 +348,19 @@ func confirm_pending_join() -> bool:
|
|||
func _set_mode(mode: Mode, clear_connection_error: bool = true) -> void:
|
||||
if clear_connection_error:
|
||||
_connection_error_latched = false
|
||||
_set_status(_default_mode_status(mode))
|
||||
_mode = mode
|
||||
_selected_entry = null
|
||||
_selected_discovery_index = -1
|
||||
_selected_friend_index = -1
|
||||
_clear_edit_state()
|
||||
_refresh_entries()
|
||||
if mode == Mode.DISCOVER and not _discovery_rooms.is_empty():
|
||||
_select_discovery_index(0)
|
||||
elif mode == Mode.FRIENDS and not _friend_entries.is_empty():
|
||||
_select_friend_index(0)
|
||||
_refresh()
|
||||
if mode == Mode.DISCOVER:
|
||||
if mode in [Mode.DISCOVER, Mode.FRIENDS]:
|
||||
_discovery_refresh_timer.start()
|
||||
_request_discovery_refresh()
|
||||
else:
|
||||
|
|
@ -262,6 +370,19 @@ func _set_mode(mode: Mode, clear_connection_error: bool = true) -> void:
|
|||
_defer_focus_control(_address if mode == Mode.DIRECT else _server_list)
|
||||
|
||||
|
||||
func _default_mode_status(mode: Mode) -> String:
|
||||
match mode:
|
||||
Mode.FRIENDS:
|
||||
return "friend status is live and not stored by discovery"
|
||||
Mode.DIRECT:
|
||||
return "direct connection • default port 7777"
|
||||
Mode.SAVED:
|
||||
return "saved servers are stored on this device"
|
||||
Mode.RECENT:
|
||||
return "recent connections are stored on this device"
|
||||
return "looking for public rooms…"
|
||||
|
||||
|
||||
func _request_join() -> void:
|
||||
_connection_error_latched = false
|
||||
if _network_session.state in [
|
||||
|
|
@ -271,8 +392,22 @@ func _request_join() -> void:
|
|||
_network_session.reset_failure()
|
||||
var endpoint_text: String = _address.text
|
||||
var discovery_room: Dictionary = {}
|
||||
if _mode == Mode.DISCOVER:
|
||||
var room: Dictionary = _selected_discovery_room()
|
||||
if _mode in [Mode.DISCOVER, Mode.FRIENDS]:
|
||||
var room: Dictionary = (
|
||||
_selected_discovery_room()
|
||||
if _mode == Mode.DISCOVER
|
||||
else _selected_friend_room()
|
||||
)
|
||||
if _mode == Mode.FRIENDS:
|
||||
var friend := _selected_friend()
|
||||
if not bool(friend.get("online", false)):
|
||||
_set_status("This person needs to be online to do this.", true)
|
||||
return
|
||||
if room.is_empty():
|
||||
_set_status(
|
||||
"This friend is online but is not in a joinable room.", true
|
||||
)
|
||||
return
|
||||
if _discovery != null and _discovery.is_own_room(room):
|
||||
_set_status("You are already hosting this room.", true)
|
||||
return
|
||||
|
|
@ -296,7 +431,9 @@ func _request_join() -> void:
|
|||
join_confirmation_requested.emit(endpoint.normalized_display)
|
||||
return
|
||||
if not discovery_room.is_empty():
|
||||
_owns_pending_public_join = true
|
||||
if not _discovery.prepare_public_join(discovery_room):
|
||||
_owns_pending_public_join = false
|
||||
_set_status("Could not prepare the public connection.", true)
|
||||
return
|
||||
_set_status("Connecting…")
|
||||
|
|
@ -305,7 +442,10 @@ func _request_join() -> void:
|
|||
|
||||
|
||||
func _on_public_join_prepared(endpoint_text: String) -> void:
|
||||
if _mode != Mode.DISCOVER or not is_visible_in_tree():
|
||||
if not _owns_pending_public_join:
|
||||
return
|
||||
_owns_pending_public_join = false
|
||||
if _mode not in [Mode.DISCOVER, Mode.FRIENDS] or not is_visible_in_tree():
|
||||
return
|
||||
var endpoint: ConnectionEndpoint = EndpointParser.parse(endpoint_text)
|
||||
if not endpoint.is_valid():
|
||||
|
|
@ -317,8 +457,13 @@ func _on_public_join_prepared(endpoint_text: String) -> void:
|
|||
|
||||
|
||||
func _on_public_join_status_changed(message: String, is_error: bool) -> void:
|
||||
if _mode == Mode.DISCOVER and is_visible_in_tree():
|
||||
if (
|
||||
_owns_pending_public_join
|
||||
and _mode in [Mode.DISCOVER, Mode.FRIENDS]
|
||||
and is_visible_in_tree()
|
||||
):
|
||||
if is_error:
|
||||
_owns_pending_public_join = false
|
||||
_connection_error_latched = true
|
||||
elif _connection_error_latched:
|
||||
return
|
||||
|
|
@ -473,6 +618,11 @@ func _on_list_item_selected(index: int) -> void:
|
|||
return
|
||||
_refresh()
|
||||
return
|
||||
if _mode == Mode.FRIENDS:
|
||||
if not _select_friend_index(index):
|
||||
return
|
||||
_refresh()
|
||||
return
|
||||
if index < 0 or index >= _visible_entries.size():
|
||||
return
|
||||
_selected_entry = _visible_entries[index]
|
||||
|
|
@ -518,6 +668,8 @@ func _restore_entry_selection_and_focus(
|
|||
|
||||
func _current_mode_button() -> Button:
|
||||
match _mode:
|
||||
Mode.FRIENDS:
|
||||
return _friends_button
|
||||
Mode.DIRECT:
|
||||
return _direct_button
|
||||
Mode.SAVED:
|
||||
|
|
@ -536,6 +688,15 @@ func _select_discovery_index(index: int) -> bool:
|
|||
return true
|
||||
|
||||
|
||||
func _select_friend_index(index: int) -> bool:
|
||||
if index < 0 or index >= _friend_entries.size():
|
||||
return false
|
||||
_selected_friend_index = index
|
||||
_selected_entry = null
|
||||
_server_list.select(index)
|
||||
return true
|
||||
|
||||
|
||||
func _refresh_entries() -> void:
|
||||
_visible_entries.clear()
|
||||
_server_list.clear()
|
||||
|
|
@ -556,6 +717,25 @@ func _refresh_entries() -> void:
|
|||
]
|
||||
)
|
||||
return
|
||||
if _mode == Mode.FRIENDS:
|
||||
for friend: Dictionary in _friend_entries:
|
||||
var room: Dictionary = (
|
||||
friend.get("room", {})
|
||||
if typeof(friend.get("room", {})) == TYPE_DICTIONARY
|
||||
else {}
|
||||
)
|
||||
var state := "offline"
|
||||
if bool(friend.get("online", false)):
|
||||
state = (
|
||||
"playing in %s"
|
||||
% str(room.get("room_name", "a public room"))
|
||||
if not room.is_empty()
|
||||
else "online"
|
||||
)
|
||||
_server_list.add_item("%s — %s" % [
|
||||
str(friend.get("display_name", "Player")), state,
|
||||
])
|
||||
return
|
||||
if _saved_servers == null or _mode == Mode.DIRECT:
|
||||
return
|
||||
_visible_entries = (
|
||||
|
|
@ -594,15 +774,24 @@ func _refresh() -> void:
|
|||
] or (_discovery != null and _discovery.is_public_join_preparing())
|
||||
var direct: bool = _mode == Mode.DIRECT
|
||||
var discovery_mode: bool = _mode == Mode.DISCOVER
|
||||
var friends_mode: bool = _mode == Mode.FRIENDS
|
||||
var selected: bool = (
|
||||
_selected_discovery_index >= 0
|
||||
if discovery_mode
|
||||
else _selected_friend_index >= 0
|
||||
if friends_mode
|
||||
else _selected_entry != null
|
||||
)
|
||||
_discover_button.button_pressed = discovery_mode
|
||||
_direct_button.button_pressed = direct
|
||||
_saved_button.button_pressed = _mode == Mode.SAVED
|
||||
_recent_button.button_pressed = _mode == Mode.RECENT
|
||||
_discover_button.set_selected(discovery_mode, false)
|
||||
_friends_button.set_selected(friends_mode, false)
|
||||
_direct_button.set_selected(direct, false)
|
||||
_saved_button.set_selected(_mode == Mode.SAVED, false)
|
||||
_recent_button.set_selected(_mode == Mode.RECENT, false)
|
||||
var direct_content_visible: bool = direct or _name_entry_active
|
||||
var list_content_visible: bool = not direct_content_visible
|
||||
_direct_content.visible = direct_content_visible
|
||||
_list_content.visible = list_content_visible
|
||||
_list_title.text = _current_list_title()
|
||||
_address.visible = direct or _name_entry_active
|
||||
_address_label.visible = _address.visible
|
||||
_address_helper.visible = _address.visible
|
||||
|
|
@ -613,15 +802,20 @@ func _refresh() -> void:
|
|||
_name_edit.visible = _name_entry_active
|
||||
_name_label.visible = _name_entry_active
|
||||
_name_helper.visible = _name_entry_active
|
||||
_server_list.visible = not direct and not _name_entry_active
|
||||
_details.visible = not direct and not _name_entry_active
|
||||
_server_list.visible = list_content_visible
|
||||
_details_panel.visible = list_content_visible
|
||||
_details.visible = list_content_visible
|
||||
_join_button.disabled = (
|
||||
connecting
|
||||
or (not direct and not selected)
|
||||
or (
|
||||
discovery_mode
|
||||
(discovery_mode or friends_mode)
|
||||
and selected
|
||||
and _discovery_room_is_full(_selected_discovery_room())
|
||||
and _discovery_room_is_full(
|
||||
_selected_discovery_room()
|
||||
if discovery_mode
|
||||
else _selected_friend_room()
|
||||
)
|
||||
)
|
||||
or (
|
||||
discovery_mode
|
||||
|
|
@ -629,16 +823,19 @@ func _refresh() -> void:
|
|||
and _discovery != null
|
||||
and _discovery.is_own_room(_selected_discovery_room())
|
||||
)
|
||||
or (friends_mode and selected and _selected_friend_room().is_empty())
|
||||
)
|
||||
_join_button.text = "join now" if direct else "join"
|
||||
_refresh_button.visible = (
|
||||
(discovery_mode or friends_mode) and not _name_entry_active
|
||||
)
|
||||
_join_button.text = "join\nnow" if direct else "join"
|
||||
_refresh_button.visible = discovery_mode and not _name_entry_active
|
||||
_save_button.visible = (
|
||||
direct or _mode == Mode.RECENT or _name_entry_active
|
||||
)
|
||||
_save_button.disabled = connecting or (
|
||||
_mode == Mode.RECENT and not selected and not _name_entry_active
|
||||
)
|
||||
_save_button.text = "save" if _name_entry_active else "save\nserver"
|
||||
_save_button.text = "save" if _name_entry_active else "save server"
|
||||
_edit_button.visible = _mode == Mode.SAVED and selected
|
||||
_favorite_button.visible = _mode == Mode.SAVED and selected
|
||||
_favorite_button.text = (
|
||||
|
|
@ -676,16 +873,22 @@ func _refresh() -> void:
|
|||
_details.text = (
|
||||
_format_discovery_details(_selected_discovery_room())
|
||||
if discovery_mode
|
||||
else _format_friend_details(_selected_friend())
|
||||
if friends_mode
|
||||
else _format_entry_details(_selected_entry)
|
||||
)
|
||||
elif (
|
||||
_discovery_rooms.is_empty()
|
||||
if discovery_mode
|
||||
else _friend_entries.is_empty()
|
||||
if friends_mode
|
||||
else _visible_entries.is_empty()
|
||||
):
|
||||
_details.text = (
|
||||
"No public rooms are available."
|
||||
if discovery_mode
|
||||
else "No friends added yet."
|
||||
if friends_mode
|
||||
else "No saved servers yet."
|
||||
if _mode == Mode.SAVED
|
||||
else "No recent connections yet."
|
||||
|
|
@ -702,9 +905,21 @@ func _refresh() -> void:
|
|||
_configure_controller_navigation()
|
||||
|
||||
|
||||
func _current_list_title() -> String:
|
||||
match _mode:
|
||||
Mode.FRIENDS:
|
||||
return "friends"
|
||||
Mode.SAVED:
|
||||
return "saved servers"
|
||||
Mode.RECENT:
|
||||
return "recent connections"
|
||||
return "public rooms"
|
||||
|
||||
|
||||
func _configure_controller_navigation() -> void:
|
||||
var mode_buttons: Array[Control] = [
|
||||
_discover_button,
|
||||
_friends_button,
|
||||
_direct_button,
|
||||
_saved_button,
|
||||
_recent_button,
|
||||
|
|
@ -722,7 +937,6 @@ func _configure_controller_navigation() -> void:
|
|||
_favorite_button,
|
||||
_delete_button,
|
||||
_cancel_button,
|
||||
_back_button,
|
||||
]:
|
||||
if _controller_focus_eligible(control):
|
||||
action_controls.append(control)
|
||||
|
|
@ -730,6 +944,7 @@ func _configure_controller_navigation() -> void:
|
|||
all_controls.append_array(mode_buttons)
|
||||
all_controls.append_array(content_controls)
|
||||
all_controls.append_array(action_controls)
|
||||
all_controls.append(_back_button)
|
||||
for control: Control in all_controls:
|
||||
control.focus_mode = Control.FOCUS_ALL
|
||||
for control: Control in [
|
||||
|
|
@ -743,10 +958,10 @@ func _configure_controller_navigation() -> void:
|
|||
_favorite_button,
|
||||
_delete_button,
|
||||
_cancel_button,
|
||||
_back_button,
|
||||
]:
|
||||
if control not in all_controls:
|
||||
control.focus_mode = Control.FOCUS_NONE
|
||||
_back_button.focus_mode = Control.FOCUS_ALL
|
||||
var primary_content: Control = (
|
||||
content_controls.front()
|
||||
if not content_controls.is_empty()
|
||||
|
|
@ -775,7 +990,7 @@ func _configure_controller_navigation() -> void:
|
|||
if index < content_controls.size() - 1
|
||||
else action_controls.front()
|
||||
if not action_controls.is_empty()
|
||||
else content
|
||||
else _back_button
|
||||
)
|
||||
_set_controller_neighbors(content, content, content, above, below)
|
||||
for index: int in action_controls.size():
|
||||
|
|
@ -787,8 +1002,22 @@ func _configure_controller_navigation() -> void:
|
|||
content_controls.back()
|
||||
if not content_controls.is_empty()
|
||||
else mode_buttons[int(_mode)],
|
||||
action,
|
||||
_back_button,
|
||||
)
|
||||
var back_above: Control = (
|
||||
action_controls.back()
|
||||
if not action_controls.is_empty()
|
||||
else content_controls.back()
|
||||
if not content_controls.is_empty()
|
||||
else mode_buttons[int(_mode)]
|
||||
)
|
||||
_set_controller_neighbors(
|
||||
_back_button,
|
||||
_back_button,
|
||||
_back_button,
|
||||
back_above,
|
||||
_back_button,
|
||||
)
|
||||
ControllerFocusNavigationType.configure_traversal(all_controls)
|
||||
_recover_controller_focus(all_controls, primary_content)
|
||||
|
||||
|
|
@ -920,6 +1149,29 @@ func _format_discovery_details(room: Dictionary) -> String:
|
|||
return "\n".join(lines)
|
||||
|
||||
|
||||
func _format_friend_details(friend: Dictionary) -> String:
|
||||
if friend.is_empty():
|
||||
return "Select a friend."
|
||||
var room := _selected_friend_room()
|
||||
var lines: Array[String] = [
|
||||
"Friend: %s" % str(friend.get("display_name", "Player")),
|
||||
"Status: %s" % (
|
||||
"online" if bool(friend.get("online", false)) else "offline"
|
||||
),
|
||||
]
|
||||
if not room.is_empty():
|
||||
lines.append("Room: %s" % str(room.get("room_name", "Public room")))
|
||||
lines.append("Players: %d / %d" % [
|
||||
int(room.get("current_players", 0)),
|
||||
int(room.get("max_players", 0)),
|
||||
])
|
||||
elif bool(friend.get("online", false)):
|
||||
lines.append("This friend is not in a joinable public room.")
|
||||
else:
|
||||
lines.append("This person needs to be online to join them.")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
func _selected_discovery_room() -> Dictionary:
|
||||
if (
|
||||
_selected_discovery_index < 0
|
||||
|
|
@ -929,6 +1181,25 @@ func _selected_discovery_room() -> Dictionary:
|
|||
return _discovery_rooms[_selected_discovery_index]
|
||||
|
||||
|
||||
func _selected_friend() -> Dictionary:
|
||||
if (
|
||||
_selected_friend_index < 0
|
||||
or _selected_friend_index >= _friend_entries.size()
|
||||
):
|
||||
return {}
|
||||
return _friend_entries[_selected_friend_index]
|
||||
|
||||
|
||||
func _selected_friend_room() -> Dictionary:
|
||||
var friend := _selected_friend()
|
||||
var room: Variant = friend.get("room", {})
|
||||
return (
|
||||
(room as Dictionary)
|
||||
if typeof(room) == TYPE_DICTIONARY
|
||||
else {}
|
||||
)
|
||||
|
||||
|
||||
func _discovery_room_is_full(room: Dictionary) -> bool:
|
||||
if room.is_empty():
|
||||
return false
|
||||
|
|
@ -938,11 +1209,14 @@ func _discovery_room_is_full(room: Dictionary) -> bool:
|
|||
func _request_discovery_refresh() -> void:
|
||||
if (
|
||||
_discovery == null
|
||||
or _mode != Mode.DISCOVER
|
||||
or _mode not in [Mode.DISCOVER, Mode.FRIENDS]
|
||||
or not is_visible_in_tree()
|
||||
):
|
||||
return
|
||||
_discovery.request_rooms()
|
||||
if _mode == Mode.DISCOVER:
|
||||
_discovery.request_rooms()
|
||||
else:
|
||||
_discovery.request_friend_presence()
|
||||
|
||||
|
||||
func _on_discovery_rooms_updated(rooms: Array[Dictionary]) -> void:
|
||||
|
|
@ -972,6 +1246,30 @@ func _on_discovery_status_changed(message: String, is_error: bool) -> void:
|
|||
_set_status(message, is_error)
|
||||
|
||||
|
||||
func _on_friend_presence_updated(friends: Array[Dictionary]) -> void:
|
||||
var selected_fingerprint := str(
|
||||
_selected_friend().get("fingerprint", "")
|
||||
)
|
||||
_friend_entries = friends.duplicate(true)
|
||||
_selected_friend_index = -1
|
||||
if _mode != Mode.FRIENDS:
|
||||
return
|
||||
_refresh_entries()
|
||||
if not selected_fingerprint.is_empty():
|
||||
for index: int in _friend_entries.size():
|
||||
if str(_friend_entries[index].get("fingerprint", "")) == selected_fingerprint:
|
||||
_select_friend_index(index)
|
||||
break
|
||||
if _selected_friend_index < 0 and not _friend_entries.is_empty():
|
||||
_select_friend_index(0)
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_social_status_changed(message: String, is_error: bool) -> void:
|
||||
if _mode == Mode.FRIENDS and is_visible_in_tree():
|
||||
_set_status(message, is_error)
|
||||
|
||||
|
||||
func _format_result_code(result_code: String) -> String:
|
||||
match result_code.strip_edges().to_upper():
|
||||
"SUCCESS":
|
||||
|
|
|
|||
|
|
@ -1,76 +1,10 @@
|
|||
[gd_scene load_steps=12 format=3]
|
||||
[gd_scene load_steps=5 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/network/join_game_page.gd" id="1_script"]
|
||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||
[ext_resource type="Script" path="res://ui/components/organizer_tab.gd" id="3_tab"]
|
||||
[ext_resource type="PackedScene" path="res://ui/components/bubble_menu/bubble_confirmation_page.tscn" id="4_confirmation"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_paper"]
|
||||
bg_color = Color(0.051, 0.173, 0.227, 1)
|
||||
corner_radius_top_left = 54
|
||||
corner_radius_top_right = 46
|
||||
corner_radius_bottom_right = 58
|
||||
corner_radius_bottom_left = 48
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_list"]
|
||||
bg_color = Color(0.071, 0.247, 0.306, 1)
|
||||
corner_radius_top_left = 12
|
||||
corner_radius_top_right = 9
|
||||
corner_radius_bottom_right = 13
|
||||
corner_radius_bottom_left = 10
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_selected"]
|
||||
bg_color = Color(0.137, 0.525, 0.592, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 7
|
||||
corner_radius_bottom_right = 9
|
||||
corner_radius_bottom_left = 6
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBox_focus"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_input"]
|
||||
content_margin_left = 12.0
|
||||
content_margin_top = 7.0
|
||||
content_margin_right = 12.0
|
||||
content_margin_bottom = 7.0
|
||||
bg_color = Color(0.031, 0.122, 0.169, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 7
|
||||
corner_radius_bottom_right = 9
|
||||
corner_radius_bottom_left = 6
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_input_focus"]
|
||||
content_margin_left = 12.0
|
||||
content_margin_top = 7.0
|
||||
content_margin_right = 12.0
|
||||
content_margin_bottom = 7.0
|
||||
bg_color = Color(0.137, 0.525, 0.592, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 7
|
||||
corner_radius_bottom_right = 9
|
||||
corner_radius_bottom_left = 6
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_input_read_only"]
|
||||
content_margin_left = 12.0
|
||||
content_margin_top = 7.0
|
||||
content_margin_right = 12.0
|
||||
content_margin_bottom = 7.0
|
||||
bg_color = Color(0.031, 0.122, 0.169, 0.82)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 7
|
||||
corner_radius_bottom_right = 9
|
||||
corner_radius_bottom_left = 6
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_status"]
|
||||
content_margin_left = 10.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 10.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(0.071, 0.247, 0.306, 0.9)
|
||||
corner_radius_top_left = 7
|
||||
corner_radius_top_right = 6
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 5
|
||||
|
||||
[node name="JoinGamePage" type="Control"]
|
||||
visible = false
|
||||
layout_mode = 3
|
||||
|
|
@ -82,243 +16,322 @@ grow_vertical = 2
|
|||
theme = ExtResource("2_theme")
|
||||
script = ExtResource("1_script")
|
||||
|
||||
[node name="Paper" type="PanelContainer" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 230.0
|
||||
offset_top = 64.0
|
||||
offset_right = 1050.0
|
||||
offset_bottom = 656.0
|
||||
theme_override_styles/panel = SubResource("StyleBox_paper")
|
||||
[node name="MainPanel" type="PanelContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -440.0
|
||||
offset_top = -330.0
|
||||
offset_right = 440.0
|
||||
offset_bottom = 330.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="Paper"]
|
||||
[node name="OuterMargin" type="MarginContainer" parent="MainPanel"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 54
|
||||
theme_override_constants/margin_top = 30
|
||||
theme_override_constants/margin_right = 54
|
||||
theme_override_constants/margin_bottom = 30
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 18
|
||||
theme_override_constants/margin_right = 24
|
||||
theme_override_constants/margin_bottom = 18
|
||||
|
||||
[node name="Layout" type="VBoxContainer" parent="Paper/Margin"]
|
||||
[node name="Layout" type="VBoxContainer" parent="MainPanel/OuterMargin"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
alignment = 1
|
||||
theme_override_constants/separation = -26
|
||||
|
||||
[node name="Title" type="Label" parent="Paper/Margin/Layout"]
|
||||
[node name="Heading" type="Label" parent="MainPanel/OuterMargin/Layout"]
|
||||
custom_minimum_size = Vector2(0, 66)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1)
|
||||
theme_override_font_sizes/font_size = 30
|
||||
text = "join game"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Modes" type="HBoxContainer" parent="Paper/Margin/Layout"]
|
||||
[node name="TabBar" type="HBoxContainer" parent="MainPanel/OuterMargin/Layout"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 52)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 14
|
||||
theme_override_constants/separation = 8
|
||||
alignment = 1
|
||||
|
||||
[node name="DiscoverButton" type="Button" parent="Paper/Margin/Layout/Modes"]
|
||||
[node name="DiscoverButton" type="Button" parent="MainPanel/OuterMargin/Layout/TabBar"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 72)
|
||||
custom_minimum_size = Vector2(165, 52)
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
focus_mode = 2
|
||||
text = "discover"
|
||||
script = ExtResource("3_tab")
|
||||
|
||||
[node name="DirectButton" type="Button" parent="Paper/Margin/Layout/Modes"]
|
||||
[node name="FriendsButton" type="Button" parent="MainPanel/OuterMargin/Layout/TabBar"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 72)
|
||||
custom_minimum_size = Vector2(140, 52)
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
focus_mode = 2
|
||||
text = "friends"
|
||||
script = ExtResource("3_tab")
|
||||
palette_index = 1
|
||||
|
||||
[node name="DirectButton" type="Button" parent="MainPanel/OuterMargin/Layout/TabBar"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(140, 52)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "direct"
|
||||
script = ExtResource("3_tab")
|
||||
palette_index = 1
|
||||
|
||||
[node name="SavedButton" type="Button" parent="Paper/Margin/Layout/Modes"]
|
||||
[node name="SavedButton" type="Button" parent="MainPanel/OuterMargin/Layout/TabBar"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 72)
|
||||
custom_minimum_size = Vector2(140, 52)
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
focus_mode = 2
|
||||
text = "saved"
|
||||
script = ExtResource("3_tab")
|
||||
palette_index = 2
|
||||
|
||||
[node name="RecentButton" type="Button" parent="Paper/Margin/Layout/Modes"]
|
||||
[node name="RecentButton" type="Button" parent="MainPanel/OuterMargin/Layout/TabBar"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 72)
|
||||
custom_minimum_size = Vector2(140, 52)
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
focus_mode = 2
|
||||
text = "recent"
|
||||
script = ExtResource("3_tab")
|
||||
palette_index = 1
|
||||
|
||||
[node name="AddressLabel" type="Label" parent="Paper/Margin/Layout"]
|
||||
[node name="ContentPanel" type="PanelContainer" parent="MainPanel/OuterMargin/Layout"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 474)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1)
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ContentMargin" type="MarginContainer" parent="MainPanel/OuterMargin/Layout/ContentPanel"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 28
|
||||
theme_override_constants/margin_top = 20
|
||||
theme_override_constants/margin_right = 28
|
||||
theme_override_constants/margin_bottom = 20
|
||||
|
||||
[node name="ContentLayout" type="VBoxContainer" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="PageStack" type="Control" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ListContent" type="VBoxContainer" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="ListTitle" type="Label" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/ListContent"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 28)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "public rooms"
|
||||
|
||||
[node name="ServerList" type="ItemList" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/ListContent"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 180)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 17
|
||||
text = "server address"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Address" type="LineEdit" parent="Paper/Margin/Layout"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 42)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.96, 0.93, 0.82, 1)
|
||||
theme_override_colors/font_uneditable_color = Color(0.624, 0.812, 0.824, 1)
|
||||
theme_override_colors/font_selected_color = Color(1, 0.98, 0.9, 1)
|
||||
theme_override_colors/font_placeholder_color = Color(0.624, 0.812, 0.824, 0.78)
|
||||
theme_override_colors/caret_color = Color(0.902, 0.969, 0.969, 1)
|
||||
theme_override_colors/selection_color = Color(0.2, 0.48, 0.59, 0.9)
|
||||
theme_override_font_sizes/font_size = 19
|
||||
theme_override_styles/normal = SubResource("StyleBox_input")
|
||||
theme_override_styles/focus = SubResource("StyleBox_input_focus")
|
||||
theme_override_styles/read_only = SubResource("StyleBox_input_read_only")
|
||||
placeholder_text = "example.net or 192.168.1.50:7777"
|
||||
alignment = 1
|
||||
max_length = 300
|
||||
|
||||
[node name="AddressHelper" type="Label" parent="Paper/Margin/Layout"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1)
|
||||
theme_override_font_sizes/font_size = 13
|
||||
text = "Hostname, IPv4, or [IPv6]. Port 7777 is used when omitted; include the port shown by a host when it differs.\nJoin Now connects once; Save Server stores this address locally."
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="NameLabel" type="Label" parent="Paper/Margin/Layout"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1)
|
||||
theme_override_font_sizes/font_size = 17
|
||||
text = "server name"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="NameEdit" type="LineEdit" parent="Paper/Margin/Layout"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 42)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.96, 0.93, 0.82, 1)
|
||||
theme_override_colors/font_uneditable_color = Color(0.624, 0.812, 0.824, 1)
|
||||
theme_override_colors/font_selected_color = Color(1, 0.98, 0.9, 1)
|
||||
theme_override_colors/font_placeholder_color = Color(0.624, 0.812, 0.824, 0.78)
|
||||
theme_override_colors/caret_color = Color(0.902, 0.969, 0.969, 1)
|
||||
theme_override_colors/selection_color = Color(0.2, 0.48, 0.59, 0.9)
|
||||
theme_override_font_sizes/font_size = 19
|
||||
theme_override_styles/normal = SubResource("StyleBox_input")
|
||||
theme_override_styles/focus = SubResource("StyleBox_input_focus")
|
||||
theme_override_styles/read_only = SubResource("StyleBox_input_read_only")
|
||||
placeholder_text = "Friend's server"
|
||||
alignment = 1
|
||||
max_length = 80
|
||||
|
||||
[node name="NameHelper" type="Label" parent="Paper/Margin/Layout"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1)
|
||||
theme_override_font_sizes/font_size = 13
|
||||
text = "Only visible on this device."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="ServerList" type="ItemList" parent="Paper/Margin/Layout"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 145)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1)
|
||||
theme_override_colors/font_selected_color = Color(0.902, 0.969, 0.969, 1)
|
||||
theme_override_colors/guide_color = Color(0.624, 0.812, 0.824, 0.22)
|
||||
theme_override_font_sizes/font_size = 17
|
||||
theme_override_styles/panel = SubResource("StyleBox_list")
|
||||
theme_override_styles/focus = SubResource("StyleBox_focus")
|
||||
theme_override_styles/selected = SubResource("StyleBox_selected")
|
||||
theme_override_styles/selected_focus = SubResource("StyleBox_selected")
|
||||
allow_reselect = true
|
||||
same_column_width = true
|
||||
|
||||
[node name="Details" type="Label" parent="Paper/Margin/Layout"]
|
||||
[node name="DetailsPanel" type="PanelContainer" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/ListContent"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 92)
|
||||
custom_minimum_size = Vector2(0, 100)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1)
|
||||
theme_override_font_sizes/font_size = 16
|
||||
|
||||
[node name="Details" type="Label" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/ListContent/DetailsPanel"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 15
|
||||
text = "Select a server."
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="Status" type="Label" parent="Paper/Margin/Layout"]
|
||||
[node name="DirectContent" type="VBoxContainer" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 38)
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="AddressLabel" type="Label" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/DirectContent"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 28)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1)
|
||||
theme_override_font_sizes/font_size = 15
|
||||
theme_override_styles/normal = SubResource("StyleBox_status")
|
||||
text = "Direct UDP connection • default port 7777"
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "server address"
|
||||
|
||||
[node name="Address" type="LineEdit" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/DirectContent"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 46)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
theme_override_font_sizes/font_size = 19
|
||||
placeholder_text = "example.net or 192.168.1.50:7777"
|
||||
alignment = 1
|
||||
max_length = 300
|
||||
|
||||
[node name="AddressHelper" type="Label" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/DirectContent"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 48)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 13
|
||||
text = "Hostname, IPv4, or [IPv6]. Port 7777 is used when omitted; include the port shown by a host when it differs.\nJoin Now connects once; Save Server stores this address locally."
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="SessionSummary" type="Label" parent="Paper/Margin/Layout"]
|
||||
[node name="NameLabel" type="Label" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/DirectContent"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 24)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "server name"
|
||||
|
||||
[node name="NameEdit" type="LineEdit" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/DirectContent"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 46)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
theme_override_font_sizes/font_size = 19
|
||||
placeholder_text = "Friend's server"
|
||||
alignment = 1
|
||||
max_length = 80
|
||||
|
||||
[node name="NameHelper" type="Label" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/DirectContent"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1)
|
||||
theme_override_font_sizes/font_size = 15
|
||||
text = "1 / 8 players"
|
||||
theme_override_font_sizes/font_size = 13
|
||||
text = "Only visible on this device."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Actions" type="HBoxContainer" parent="Paper/Margin/Layout"]
|
||||
[node name="Spacer" type="Control" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/DirectContent"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 7
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Actions" type="HBoxContainer" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 46)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="RefreshButton" type="Button" parent="Paper/Margin/Layout/Actions"]
|
||||
[node name="RefreshButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/Actions"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(82, 72)
|
||||
custom_minimum_size = Vector2(120, 46)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "refresh"
|
||||
|
||||
[node name="JoinButton" type="Button" parent="Paper/Margin/Layout/Actions"]
|
||||
[node name="JoinButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/Actions"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(82, 72)
|
||||
custom_minimum_size = Vector2(120, 46)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "join"
|
||||
|
||||
[node name="SaveButton" type="Button" parent="Paper/Margin/Layout/Actions"]
|
||||
[node name="SaveButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/Actions"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(82, 72)
|
||||
custom_minimum_size = Vector2(130, 46)
|
||||
layout_mode = 2
|
||||
text = "save\nserver"
|
||||
focus_mode = 2
|
||||
text = "save server"
|
||||
|
||||
[node name="EditButton" type="Button" parent="Paper/Margin/Layout/Actions"]
|
||||
[node name="EditButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/Actions"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(76, 68)
|
||||
custom_minimum_size = Vector2(110, 46)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "edit"
|
||||
|
||||
[node name="FavoriteButton" type="Button" parent="Paper/Margin/Layout/Actions"]
|
||||
[node name="FavoriteButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/Actions"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(88, 68)
|
||||
custom_minimum_size = Vector2(130, 46)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "favorite"
|
||||
|
||||
[node name="DeleteButton" type="Button" parent="Paper/Margin/Layout/Actions"]
|
||||
[node name="DeleteButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/Actions"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(82, 68)
|
||||
custom_minimum_size = Vector2(110, 46)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "delete"
|
||||
|
||||
[node name="CancelButton" type="Button" parent="Paper/Margin/Layout/Actions"]
|
||||
[node name="CancelButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/Actions"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(76, 68)
|
||||
custom_minimum_size = Vector2(120, 46)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "cancel"
|
||||
|
||||
[node name="BackButton" type="Button" parent="Paper/Margin/Layout/Actions"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(76, 68)
|
||||
[node name="FooterGroup" type="MarginContainer" parent="MainPanel/OuterMargin/Layout"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_top = 34
|
||||
|
||||
[node name="FooterLayout" type="VBoxContainer" parent="MainPanel/OuterMargin/Layout/FooterGroup"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="InfoRow" type="HBoxContainer" parent="MainPanel/OuterMargin/Layout/FooterGroup/FooterLayout"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="Status" type="Label" parent="MainPanel/OuterMargin/Layout/FooterGroup/FooterLayout/InfoRow"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 24)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "looking for public rooms…"
|
||||
vertical_alignment = 1
|
||||
text_overrun_behavior = 3
|
||||
|
||||
[node name="SessionSummary" type="Label" parent="MainPanel/OuterMargin/Layout/FooterGroup/FooterLayout/InfoRow"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(210, 24)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "1 / 8 players"
|
||||
horizontal_alignment = 2
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Footer" type="HBoxContainer" parent="MainPanel/OuterMargin/Layout/FooterGroup/FooterLayout"]
|
||||
layout_mode = 2
|
||||
alignment = 2
|
||||
|
||||
[node name="BackButton" type="Button" parent="MainPanel/OuterMargin/Layout/FooterGroup/FooterLayout/Footer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(150, 46)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "back"
|
||||
|
||||
[node name="DeleteConfirmation" parent="." instance=ExtResource("4_confirmation")]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue