feat(ui): wire M8 social, M9 economy, and M10 discovery into main menu and in-game HUD
checkpoint / checkpoint (push) Successful in 5m54s

This commit is contained in:
2026-09-17 18:04:47 +00:00
parent ea6948a9ba
commit e05e18c384
10 changed files with 382 additions and 23 deletions
+5
View File
@@ -45,6 +45,11 @@ custom_minimum_size = Vector2(100, 36)
layout_mode = 2 layout_mode = 2
text = "Refresh" text = "Refresh"
[node name="WorldListButton" type="Button" parent="VBox/Header"]
custom_minimum_size = Vector2(150, 36)
layout_mode = 2
text = "🏗️ World Browser"
[node name="StatusLabel" type="Label" parent="VBox"] [node name="StatusLabel" type="Label" parent="VBox"]
visible = false visible = false
layout_mode = 2 layout_mode = 2
+64 -4
View File
@@ -107,26 +107,86 @@ visible = false
text = "" text = ""
horizontal_alignment = 1 horizontal_alignment = 1
[node name="PlayButton" type="Button" parent="CenterContainer/VBoxContainer"] [node name="DiscoverButton" type="Button" parent="CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(340, 38)
layout_mode = 1 layout_mode = 1
text = "Play" text = "🗺️ Discover Experiences"
size_flags_horizontal = 4 size_flags_horizontal = 4
disabled = true
[node name="PlayButton" type="Button" parent="CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(340, 38)
layout_mode = 1
text = "🏗️ Worlds Browser & Editor"
size_flags_horizontal = 4
disabled = true
[node name="CustomizeButton" type="Button" parent="CenterContainer/VBoxContainer"] [node name="CustomizeButton" type="Button" parent="CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(340, 38)
layout_mode = 1 layout_mode = 1
text = "Customize Avatar" text = "👤 Customize Avatar"
size_flags_horizontal = 4
disabled = true
[node name="SocialButton" type="Button" parent="CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(340, 38)
layout_mode = 1
text = "👥 Friends & Presence"
size_flags_horizontal = 4
disabled = true
[node name="ChatButton" type="Button" parent="CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(340, 38)
layout_mode = 1
text = "💬 Chat & Messages"
size_flags_horizontal = 4
disabled = true
[node name="MarketplaceButton" type="Button" parent="CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(340, 38)
layout_mode = 1
text = "🛒 Marketplace"
size_flags_horizontal = 4
disabled = true
[node name="WalletButton" type="Button" parent="CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(340, 38)
layout_mode = 1
text = "💰 Wallet & Currency"
size_flags_horizontal = 4
disabled = true
[node name="GroupsButton" type="Button" parent="CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(340, 38)
layout_mode = 1
text = "🛡️ Groups & Clans"
size_flags_horizontal = 4
disabled = true
[node name="SellingButton" type="Button" parent="CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(340, 38)
layout_mode = 1
text = "🏷️ Creator & Selling"
size_flags_horizontal = 4 size_flags_horizontal = 4
disabled = true disabled = true
[node name="QuitButton" type="Button" parent="CenterContainer/VBoxContainer"] [node name="QuitButton" type="Button" parent="CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(340, 38)
layout_mode = 1 layout_mode = 1
text = "Quit" text = "🚪 Quit"
size_flags_horizontal = 4 size_flags_horizontal = 4
[connection signal="pressed" from="CenterContainer/VBoxContainer/AuthButtonsRow/LoginButton" to="." method="_on_login_button_pressed"] [connection signal="pressed" from="CenterContainer/VBoxContainer/AuthButtonsRow/LoginButton" to="." method="_on_login_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/AuthButtonsRow/RegisterButton" to="." method="_on_register_button_pressed"] [connection signal="pressed" from="CenterContainer/VBoxContainer/AuthButtonsRow/RegisterButton" to="." method="_on_register_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/DiscoverButton" to="." method="_on_discover_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/PlayButton" to="." method="_on_play_button_pressed"] [connection signal="pressed" from="CenterContainer/VBoxContainer/PlayButton" to="." method="_on_play_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/CustomizeButton" to="." method="_on_customize_button_pressed"] [connection signal="pressed" from="CenterContainer/VBoxContainer/CustomizeButton" to="." method="_on_customize_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/SocialButton" to="." method="_on_social_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/ChatButton" to="." method="_on_chat_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/MarketplaceButton" to="." method="_on_marketplace_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/WalletButton" to="." method="_on_wallet_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/GroupsButton" to="." method="_on_groups_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/SellingButton" to="." method="_on_selling_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/QuitButton" to="." method="_on_quit_button_pressed"] [connection signal="pressed" from="CenterContainer/VBoxContainer/QuitButton" to="." method="_on_quit_button_pressed"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/AuthButton" to="." method="_on_auth_button_pressed"] [connection signal="pressed" from="CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/AuthButton" to="." method="_on_auth_button_pressed"]
[connection signal="text_submitted" from="CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/UsernameEdit" to="." method="_on_auth_button_pressed"] [connection signal="text_submitted" from="CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/UsernameEdit" to="." method="_on_auth_button_pressed"]
+6
View File
@@ -36,6 +36,11 @@ horizontal_alignment = 1
size_flags_horizontal = 3 size_flags_horizontal = 3
theme_override_font_sizes/font_size = 28 theme_override_font_sizes/font_size = 28
[node name="DiscoverButton" type="Button" parent="VBoxContainer/HeaderHBox"]
layout_mode = 3
text = "🗺️ Discover Feed"
size_flags_horizontal = 0
[node name="CreateButton" type="Button" parent="VBoxContainer/HeaderHBox"] [node name="CreateButton" type="Button" parent="VBoxContainer/HeaderHBox"]
layout_mode = 3 layout_mode = 3
text = "+ Create World" text = "+ Create World"
@@ -80,5 +85,6 @@ horizontal_alignment = 1
theme_override_font_sizes/font_size = 18 theme_override_font_sizes/font_size = 18
[connection signal="pressed" from="VBoxContainer/HeaderHBox/BackButton" to="." method="_on_back_button_pressed"] [connection signal="pressed" from="VBoxContainer/HeaderHBox/BackButton" to="." method="_on_back_button_pressed"]
[connection signal="pressed" from="VBoxContainer/HeaderHBox/DiscoverButton" to="." method="_on_discover_button_pressed"]
[connection signal="pressed" from="VBoxContainer/HeaderHBox/CreateButton" to="." method="_on_create_button_pressed"] [connection signal="pressed" from="VBoxContainer/HeaderHBox/CreateButton" to="." method="_on_create_button_pressed"]
[connection signal="pressed" from="VBoxContainer/HeaderHBox/RefreshButton" to="." method="_on_refresh_button_pressed"] [connection signal="pressed" from="VBoxContainer/HeaderHBox/RefreshButton" to="." method="_on_refresh_button_pressed"]
+9 -1
View File
@@ -10,6 +10,7 @@ signal worlds_loaded(count: int)
@onready var back_button: Button = $VBox/Header/BackButton @onready var back_button: Button = $VBox/Header/BackButton
@onready var refresh_button: Button = $VBox/Header/RefreshButton @onready var refresh_button: Button = $VBox/Header/RefreshButton
@onready var world_list_button: Button = $VBox/Header.get_node_or_null("WorldListButton")
@onready var status_label: Label = $VBox/StatusLabel @onready var status_label: Label = $VBox/StatusLabel
# Filter Controls # Filter Controls
@@ -94,6 +95,8 @@ func _wire_signals() -> void:
prev_button.pressed.connect(_on_prev_page_pressed) prev_button.pressed.connect(_on_prev_page_pressed)
if next_button and not next_button.pressed.is_connected(_on_next_page_pressed): if next_button and not next_button.pressed.is_connected(_on_next_page_pressed):
next_button.pressed.connect(_on_next_page_pressed) next_button.pressed.connect(_on_next_page_pressed)
if world_list_button and not world_list_button.pressed.is_connected(_on_world_list_pressed):
world_list_button.pressed.connect(_on_world_list_pressed)
func load_categories() -> void: func load_categories() -> void:
@@ -372,5 +375,10 @@ func _on_next_page_pressed() -> void:
func _on_back_pressed() -> void: func _on_back_pressed() -> void:
back_requested.emit() back_requested.emit()
if get_tree(): if get_tree() and get_tree().current_scene == self:
get_tree().change_scene_to_file("res://scenes/main_menu.tscn") get_tree().change_scene_to_file("res://scenes/main_menu.tscn")
func _on_world_list_pressed() -> void:
if get_tree():
get_tree().change_scene_to_file("res://scenes/world_list.tscn")
+216 -6
View File
@@ -36,6 +36,10 @@ var _status_label: Label = null
## normal play"). Editing is in-memory only here; the save path to the ## normal play"). Editing is in-memory only here; the save path to the
## backend is a sibling task (world_persistence_impl.md). ## backend is a sibling task (world_persistence_impl.md).
var _build_editor: InGameEditor = null var _build_editor: InGameEditor = null
var _chat_overlay: Control = null
var _friends_overlay: Control = null
var _hud_layer: CanvasLayer = null
var _overlay_layer: CanvasLayer = null
var _stream_manager: ChunkStreamManager = null var _stream_manager: ChunkStreamManager = null
@@ -47,6 +51,7 @@ func _ready() -> void:
if Session.editing: if Session.editing:
_open_editor_for_world() _open_editor_for_world()
else: else:
_setup_in_game_hud()
if Session.active: if Session.active:
_setup_networked() _setup_networked()
_setup_chunk_streaming() _setup_chunk_streaming()
@@ -165,6 +170,18 @@ func _hide_default_test_props() -> void:
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(event: InputEvent) -> void:
if event is InputEventKey and event.pressed and not event.echo:
if event.keycode == KEY_T or (event.keycode == KEY_ENTER and not (_chat_overlay != null and is_instance_valid(_chat_overlay))):
if not Session.editing and not (_build_editor != null and is_instance_valid(_build_editor)):
get_viewport().set_input_as_handled()
_toggle_chat_overlay()
return
elif event.keycode == KEY_TAB or event.keycode == KEY_F1:
if not Session.editing and not (_build_editor != null and is_instance_valid(_build_editor)):
get_viewport().set_input_as_handled()
_toggle_friends_overlay()
return
if event.is_action_pressed("toggle_build_mode"): if event.is_action_pressed("toggle_build_mode"):
# world_persistence_impl.md §6 D4: the non-joined "Edit world" entry # world_persistence_impl.md §6 D4: the non-joined "Edit world" entry
# already has its editor open from _ready() -- F2 here is the # already has its editor open from _ready() -- F2 here is the
@@ -174,14 +191,14 @@ func _unhandled_input(event: InputEvent) -> void:
_toggle_build_mode() _toggle_build_mode()
return return
# ESC leaves the world back to the browser (or, while build mode is # ESC leaves the world back to the browser (or closes an open overlay/editor).
# open, just closes build mode instead). Handled here (the scene root
# that owns the connection) rather than in player.gd so the teardown is
# a single ordered path -- doing it from two places raced _on_server_lost
# against the scene change and looked like a crash.
if event.is_action_pressed("ui_cancel"): if event.is_action_pressed("ui_cancel"):
get_viewport().set_input_as_handled() get_viewport().set_input_as_handled()
if Session.editing: if _chat_overlay != null and is_instance_valid(_chat_overlay):
_close_chat_overlay()
elif _friends_overlay != null and is_instance_valid(_friends_overlay):
_close_friends_overlay()
elif Session.editing:
# Non-joined editing has no play session to fall back into -- # Non-joined editing has no play session to fall back into --
# closing it means leaving, same as the Close button. # closing it means leaving, same as the Close button.
_exit_editor_to_browser() _exit_editor_to_browser()
@@ -191,6 +208,194 @@ func _unhandled_input(event: InputEvent) -> void:
_leave_world() _leave_world()
# ─── In-Game HUD & Social Overlays ───────────────────────────────────────────
func _setup_in_game_hud() -> void:
if Session.editing:
return
if _hud_layer != null and is_instance_valid(_hud_layer):
return
_hud_layer = CanvasLayer.new()
_hud_layer.name = "InGameHUD"
_hud_layer.layer = 10
add_child(_hud_layer)
var panel := PanelContainer.new()
panel.name = "HUDPanel"
panel.position = Vector2(20, 20)
var style := StyleBoxFlat.new()
style.bg_color = Color(0.1, 0.12, 0.16, 0.8)
style.border_color = Color(0.3, 0.35, 0.45, 0.8)
style.set_border_width_all(1)
style.set_corner_radius_all(6)
style.content_margin_left = 8
style.content_margin_right = 8
style.content_margin_top = 6
style.content_margin_bottom = 6
panel.add_theme_stylebox_override("panel", style)
_hud_layer.add_child(panel)
var bar := HBoxContainer.new()
bar.name = "QuickActionBar"
bar.add_theme_constant_override("separation", 8)
panel.add_child(bar)
var chat_btn := Button.new()
chat_btn.text = "💬 Chat [T]"
chat_btn.pressed.connect(_toggle_chat_overlay)
bar.add_child(chat_btn)
var friends_btn := Button.new()
friends_btn.text = "👥 Friends [Tab]"
friends_btn.pressed.connect(_toggle_friends_overlay)
bar.add_child(friends_btn)
var build_btn := Button.new()
build_btn.text = "🔨 Build [F2]"
build_btn.pressed.connect(_toggle_build_mode)
bar.add_child(build_btn)
var exit_btn := Button.new()
exit_btn.text = "🚪 Menu [ESC]"
exit_btn.pressed.connect(func():
if _chat_overlay != null and is_instance_valid(_chat_overlay):
_close_chat_overlay()
elif _friends_overlay != null and is_instance_valid(_friends_overlay):
_close_friends_overlay()
elif _build_editor != null and is_instance_valid(_build_editor):
_close_build_mode()
else:
_leave_world()
)
bar.add_child(exit_btn)
func _toggle_chat_overlay() -> void:
if _chat_overlay != null and is_instance_valid(_chat_overlay):
_close_chat_overlay()
else:
_open_chat_overlay()
func _open_chat_overlay() -> void:
if _friends_overlay != null and is_instance_valid(_friends_overlay):
_close_friends_overlay()
if _build_editor != null and is_instance_valid(_build_editor):
_close_build_mode()
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
_player.set_physics_process(false)
_player.set_process_unhandled_input(false)
if _overlay_layer == null or not is_instance_valid(_overlay_layer):
_overlay_layer = CanvasLayer.new()
_overlay_layer.name = "OverlayLayer"
_overlay_layer.layer = 20
add_child(_overlay_layer)
var bg := ColorRect.new()
bg.name = "OverlayBG"
bg.color = Color(0.06, 0.08, 0.12, 0.9)
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
_overlay_layer.add_child(bg)
var chat_scene := load("res://scenes/chat_ui.tscn")
_chat_overlay = chat_scene.instantiate() as Control
_chat_overlay.set_anchors_preset(Control.PRESET_FULL_RECT)
bg.add_child(_chat_overlay)
# If active in a world, configure world chat automatically
if Session.world_id != null and int(Session.world_id) > 0:
var wid_edit = _chat_overlay.find_child("WorldIdEdit", true, false) as LineEdit
if wid_edit != null:
wid_edit.text = str(Session.world_id)
if _chat_overlay.has_method("switch_world_chat"):
_chat_overlay.switch_world_chat(int(Session.world_id))
if _chat_overlay.has_signal("back_requested"):
_chat_overlay.back_requested.connect(_close_chat_overlay)
var back_btn = _chat_overlay.find_child("BackButton", true, false) as Button
if back_btn != null:
back_btn.text = "✕ Close Chat"
if not back_btn.pressed.is_connected(_close_chat_overlay):
back_btn.pressed.connect(_close_chat_overlay)
var msg_input = _chat_overlay.find_child("MessageInput", true, false) as LineEdit
if msg_input != null:
msg_input.grab_focus.call_deferred()
func _close_chat_overlay() -> void:
if _chat_overlay != null and is_instance_valid(_chat_overlay):
_chat_overlay.queue_free()
_chat_overlay = null
if _overlay_layer != null and is_instance_valid(_overlay_layer):
_overlay_layer.queue_free()
_overlay_layer = null
_player.set_physics_process(true)
_player.set_process_unhandled_input(true)
_player._refresh_mouse_mode()
func _toggle_friends_overlay() -> void:
if _friends_overlay != null and is_instance_valid(_friends_overlay):
_close_friends_overlay()
else:
_open_friends_overlay()
func _open_friends_overlay() -> void:
if _chat_overlay != null and is_instance_valid(_chat_overlay):
_close_chat_overlay()
if _build_editor != null and is_instance_valid(_build_editor):
_close_build_mode()
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
_player.set_physics_process(false)
_player.set_process_unhandled_input(false)
if _overlay_layer == null or not is_instance_valid(_overlay_layer):
_overlay_layer = CanvasLayer.new()
_overlay_layer.name = "OverlayLayer"
_overlay_layer.layer = 20
add_child(_overlay_layer)
var bg := ColorRect.new()
bg.name = "OverlayBG"
bg.color = Color(0.06, 0.08, 0.12, 0.9)
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
_overlay_layer.add_child(bg)
var friends_scene := load("res://scenes/friends_presence_ui.tscn")
_friends_overlay = friends_scene.instantiate() as Control
_friends_overlay.set_anchors_preset(Control.PRESET_FULL_RECT)
bg.add_child(_friends_overlay)
if _friends_overlay.has_signal("back_requested"):
_friends_overlay.back_requested.connect(_close_friends_overlay)
var back_btn = _friends_overlay.find_child("BackButton", true, false) as Button
if back_btn != null:
back_btn.text = "✕ Close"
if not back_btn.pressed.is_connected(_close_friends_overlay):
back_btn.pressed.connect(_close_friends_overlay)
func _close_friends_overlay() -> void:
if _friends_overlay != null and is_instance_valid(_friends_overlay):
_friends_overlay.queue_free()
_friends_overlay = null
if _overlay_layer != null and is_instance_valid(_overlay_layer):
_overlay_layer.queue_free()
_overlay_layer = null
_player.set_physics_process(true)
_player.set_process_unhandled_input(true)
_player._refresh_mouse_mode()
## Entry point into the in-game editor (docs/architecture/editor.md §7.2). ## Entry point into the in-game editor (docs/architecture/editor.md §7.2).
## Freezes the player and opens a fresh, empty in-memory chunk to edit -- ## Freezes the player and opens a fresh, empty in-memory chunk to edit --
## loading a *real* chunk from the backend for editing is the sibling ## loading a *real* chunk from the backend for editing is the sibling
@@ -204,6 +409,11 @@ func _toggle_build_mode() -> void:
func _open_build_mode() -> void: func _open_build_mode() -> void:
if _chat_overlay != null and is_instance_valid(_chat_overlay):
_close_chat_overlay()
if _friends_overlay != null and is_instance_valid(_friends_overlay):
_close_friends_overlay()
var world_id_str := str(Session.world_id) if Session.world_id != null else "0" var world_id_str := str(Session.world_id) if Session.world_id != null else "0"
var room_slug := "main" var room_slug := "main"
var coord := Vector3i.ZERO var coord := Vector3i.ZERO
+72 -12
View File
@@ -39,8 +39,15 @@ var _dob_edit: LineEdit
var _auth_button: Button var _auth_button: Button
var _status_label: Label var _status_label: Label
var _welcome_label: Label var _welcome_label: Label
var _discover_button: Button
var _play_button: Button var _play_button: Button
var _customize_button: Button var _customize_button: Button
var _social_button: Button
var _chat_button: Button
var _marketplace_button: Button
var _wallet_button: Button
var _groups_button: Button
var _selling_button: Button
var _quit_button: Button var _quit_button: Button
var _is_register_mode: bool = false var _is_register_mode: bool = false
@@ -93,16 +100,22 @@ func _ready() -> void:
_auth_button = $CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/AuthButton _auth_button = $CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/AuthButton
_status_label = $CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/AuthStatusLabel _status_label = $CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/AuthStatusLabel
_welcome_label = $CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/WelcomeLabel _welcome_label = $CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/WelcomeLabel
_discover_button = $CenterContainer/VBoxContainer.get_node_or_null("DiscoverButton")
_play_button = $CenterContainer/VBoxContainer/PlayButton _play_button = $CenterContainer/VBoxContainer/PlayButton
_customize_button = $CenterContainer/VBoxContainer/CustomizeButton _customize_button = $CenterContainer/VBoxContainer/CustomizeButton
_social_button = $CenterContainer/VBoxContainer.get_node_or_null("SocialButton")
_chat_button = $CenterContainer/VBoxContainer.get_node_or_null("ChatButton")
_marketplace_button = $CenterContainer/VBoxContainer.get_node_or_null("MarketplaceButton")
_wallet_button = $CenterContainer/VBoxContainer.get_node_or_null("WalletButton")
_groups_button = $CenterContainer/VBoxContainer.get_node_or_null("GroupsButton")
_selling_button = $CenterContainer/VBoxContainer.get_node_or_null("SellingButton")
_quit_button = $CenterContainer/VBoxContainer/QuitButton _quit_button = $CenterContainer/VBoxContainer/QuitButton
# Default to Play being gated. When a token exists, the async session # Default to navigation being gated. When a token exists, the async session
# check below may take several frames (a real get_me() round-trip); # check below may take several frames (a real get_me() round-trip);
# keeping the button disabled until validation succeeds prevents a fast # keeping the button disabled until validation succeeds prevents a fast
# click from navigating to world_list with an expired/invalid token. # click from navigating to world_list with an expired/invalid token.
_play_button.disabled = true _set_nav_enabled(false)
_customize_button.disabled = true
# Check whether a valid session already exists (e.g. user logged in, # Check whether a valid session already exists (e.g. user logged in,
# navigated to world_list, then clicked Back). If so, restore the # navigated to world_list, then clicked Back). If so, restore the
@@ -122,6 +135,16 @@ func _ready() -> void:
auth_state_ready.emit() auth_state_ready.emit()
func _set_nav_enabled(enabled: bool) -> void:
var buttons = [
_discover_button, _play_button, _customize_button, _social_button,
_chat_button, _marketplace_button, _wallet_button, _groups_button, _selling_button
]
for b in buttons:
if b != null and is_instance_valid(b):
b.disabled = not enabled
## Restore the UI to the authenticated state (Play enabled, welcome message ## Restore the UI to the authenticated state (Play enabled, welcome message
## visible, auth buttons hidden). AuthPanel itself stays visible -- the ## visible, auth buttons hidden). AuthPanel itself stays visible -- the
## welcome label lives inside it, so hiding the panel hides the label too ## welcome label lives inside it, so hiding the panel hides the label too
@@ -148,8 +171,7 @@ func _show_authenticated(username: String) -> void:
_dob_edit.visible = false _dob_edit.visible = false
_auth_button.visible = false _auth_button.visible = false
_auth_mode_label.visible = false _auth_mode_label.visible = false
_play_button.disabled = false _set_nav_enabled(true)
_customize_button.disabled = false
_auth_buttons_row.visible = false _auth_buttons_row.visible = false
print("Restored authenticated session for: ", username) print("Restored authenticated session for: ", username)
@@ -163,18 +185,21 @@ func _reset_to_logged_out() -> void:
_email_edit.visible = false _email_edit.visible = false
_dob_edit.visible = false _dob_edit.visible = false
_dob_edit.text = "" _dob_edit.text = ""
_play_button.disabled = true # Gate play behind successful auth. _set_nav_enabled(false)
_customize_button.disabled = true
_update_auth_mode_ui() _update_auth_mode_ui()
# ─── Play / Quit ────────────────────────────────────────────────────────────── # ─── Navigation ───────────────────────────────────────────────────────────────
func _on_discover_button_pressed() -> void:
if _discover_button != null and _discover_button.disabled:
return
get_tree().change_scene_to_file("res://scenes/discovery_ui.tscn")
func _on_play_button_pressed() -> void: func _on_play_button_pressed() -> void:
if _play_button.disabled: if _play_button.disabled:
return return
# Browse worlds is the first real screen past login -- actual gameplay
# (joining a world) doesn't exist yet, browsing does.
get_tree().change_scene_to_file("res://scenes/world_list.tscn") get_tree().change_scene_to_file("res://scenes/world_list.tscn")
@@ -184,6 +209,42 @@ func _on_customize_button_pressed() -> void:
get_tree().change_scene_to_file("res://scenes/avatar_customization.tscn") get_tree().change_scene_to_file("res://scenes/avatar_customization.tscn")
func _on_social_button_pressed() -> void:
if _social_button != null and _social_button.disabled:
return
get_tree().change_scene_to_file("res://scenes/friends_presence_ui.tscn")
func _on_chat_button_pressed() -> void:
if _chat_button != null and _chat_button.disabled:
return
get_tree().change_scene_to_file("res://scenes/chat_ui.tscn")
func _on_marketplace_button_pressed() -> void:
if _marketplace_button != null and _marketplace_button.disabled:
return
get_tree().change_scene_to_file("res://scenes/marketplace_ui.tscn")
func _on_wallet_button_pressed() -> void:
if _wallet_button != null and _wallet_button.disabled:
return
get_tree().change_scene_to_file("res://scenes/wallet_ui.tscn")
func _on_groups_button_pressed() -> void:
if _groups_button != null and _groups_button.disabled:
return
get_tree().change_scene_to_file("res://scenes/groups_ui.tscn")
func _on_selling_button_pressed() -> void:
if _selling_button != null and _selling_button.disabled:
return
get_tree().change_scene_to_file("res://scenes/selling_payout_ui.tscn")
func _on_quit_button_pressed() -> void: func _on_quit_button_pressed() -> void:
get_tree().quit() get_tree().quit()
@@ -301,8 +362,7 @@ func _on_auth_complete(result: Dictionary) -> void:
_status_label.visible = false _status_label.visible = false
_welcome_label.text = "Welcome, %s!" % _username_edit.text.strip_edges() _welcome_label.text = "Welcome, %s!" % _username_edit.text.strip_edges()
_welcome_label.visible = true _welcome_label.visible = true
_play_button.disabled = false _set_nav_enabled(true)
_customize_button.disabled = false
_auth_buttons_row.visible = false _auth_buttons_row.visible = false
print("Auth successful. Token stored.") print("Auth successful. Token stored.")
await _prime_avatar() await _prime_avatar()
+2
View File
@@ -69,6 +69,8 @@ func _wire_signals() -> void:
func _on_back_pressed() -> void: func _on_back_pressed() -> void:
back_requested.emit() back_requested.emit()
if get_tree() and get_tree().current_scene == self:
get_tree().change_scene_to_file("res://scenes/main_menu.tscn")
func show_status(text: String, is_error: bool = false) -> void: func show_status(text: String, is_error: bool = false) -> void:
+2
View File
@@ -72,6 +72,8 @@ func _wire_signals() -> void:
func _on_back_pressed() -> void: func _on_back_pressed() -> void:
back_requested.emit() back_requested.emit()
if get_tree() and get_tree().current_scene == self:
get_tree().change_scene_to_file("res://scenes/main_menu.tscn")
func show_status(text: String, is_error: bool = false) -> void: func show_status(text: String, is_error: bool = false) -> void:
+2
View File
@@ -68,6 +68,8 @@ func _wire_signals() -> void:
func _on_back_pressed() -> void: func _on_back_pressed() -> void:
back_requested.emit() back_requested.emit()
if get_tree() and get_tree().current_scene == self:
get_tree().change_scene_to_file("res://scenes/main_menu.tscn")
func show_status(text: String, is_error: bool = false) -> void: func show_status(text: String, is_error: bool = false) -> void:
+4
View File
@@ -201,6 +201,10 @@ func _on_back_button_pressed() -> void:
get_tree().change_scene_to_file("res://scenes/main_menu.tscn") get_tree().change_scene_to_file("res://scenes/main_menu.tscn")
func _on_discover_button_pressed() -> void:
get_tree().change_scene_to_file("res://scenes/discovery_ui.tscn")
## Fetch the world list from the backend and update the UI. ## Fetch the world list from the backend and update the UI.
func _refresh_worlds() -> void: func _refresh_worlds() -> void: