feat(ui): wire M8 social, M9 economy, and M10 discovery into main menu and in-game HUD
checkpoint / checkpoint (push) Successful in 5m54s
checkpoint / checkpoint (push) Successful in 5m54s
This commit is contained in:
@@ -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
@@ -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"]
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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()
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user