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:
+216
-6
@@ -36,6 +36,10 @@ var _status_label: Label = null
|
||||
## normal play"). Editing is in-memory only here; the save path to the
|
||||
## backend is a sibling task (world_persistence_impl.md).
|
||||
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
|
||||
@@ -47,6 +51,7 @@ func _ready() -> void:
|
||||
if Session.editing:
|
||||
_open_editor_for_world()
|
||||
else:
|
||||
_setup_in_game_hud()
|
||||
if Session.active:
|
||||
_setup_networked()
|
||||
_setup_chunk_streaming()
|
||||
@@ -165,6 +170,18 @@ func _hide_default_test_props() -> 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"):
|
||||
# world_persistence_impl.md §6 D4: the non-joined "Edit world" entry
|
||||
# already has its editor open from _ready() -- F2 here is the
|
||||
@@ -174,14 +191,14 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
_toggle_build_mode()
|
||||
return
|
||||
|
||||
# ESC leaves the world back to the browser (or, while build mode is
|
||||
# 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.
|
||||
# ESC leaves the world back to the browser (or closes an open overlay/editor).
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
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 --
|
||||
# closing it means leaving, same as the Close button.
|
||||
_exit_editor_to_browser()
|
||||
@@ -191,6 +208,194 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
_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).
|
||||
## 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
|
||||
@@ -204,6 +409,11 @@ func _toggle_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 room_slug := "main"
|
||||
var coord := Vector3i.ZERO
|
||||
|
||||
Reference in New Issue
Block a user