diff --git a/scenes/lobby.gd b/scenes/lobby.gd index 9047994..73de880 100644 --- a/scenes/lobby.gd +++ b/scenes/lobby.gd @@ -2,20 +2,22 @@ extends Control # UI References - Main Menu @onready var main_menu_panel = $MainMenuPanel -@onready var main_title = $MainMenuPanel/VBoxContainer/TitleContainer/Title -@onready var main_subtitle = $MainMenuPanel/VBoxContainer/TitleContainer/Subtitle -@onready var create_room_btn = $MainMenuPanel/VBoxContainer/ButtonSection/CreateRoomBtn -@onready var browse_rooms_btn = $MainMenuPanel/VBoxContainer/ButtonSection/BrowseRoomsBtn -@onready var main_menu_profile_btn = $MainMenuPanel/VBoxContainer/ButtonSection/ProfileBtn -@onready var lobby_settings_btn = $MainMenuPanel/VBoxContainer/ButtonSection/SettingsBtn -@onready var quit_btn = $MainMenuPanel/VBoxContainer/ButtonSection/QuitBtn +@onready var main_title = $MainMenuPanel/HiddenLogic/Title +@onready var username_label = $MainMenuPanel/MainContainer/MarginContainer/LeftPanel/ProfileBox/VBoxContainer/Username +@onready var main_subtitle = $MainMenuPanel/MainContainer/MarginContainer/LeftPanel/ProfileBox/VBoxContainer/Subtitle +@onready var create_room_btn = $MainMenuPanel/MainContainer/MarginContainer/BottomRightPanel/CreateRoomBtn +@onready var browse_rooms_btn = $MainMenuPanel/MainContainer/MarginContainer/BottomRightPanel/BrowseRoomsBtn +@onready var tutorial_btn = $MainMenuPanel/MainContainer/MarginContainer/BottomRightPanel/TutorialBtn +@onready var main_menu_profile_btn = $MainMenuPanel/MainContainer/MarginContainer/LeftPanel/ProfileBox/ProfileBtn +@onready var lobby_settings_btn = $MainMenuPanel/MainContainer/MarginContainer/TopRightPanel/SettingsBtn +@onready var quit_btn = $MainMenuPanel/MainContainer/MarginContainer/LeftPanel/HBoxContainer/QuitBtn # UI References - Server Selection -@onready var server_option = $MainMenuPanel/VBoxContainer/ServerSelectionSection/ServerOption -@onready var server_ip_input = $MainMenuPanel/VBoxContainer/ServerSelectionSection/ServerIPInput +@onready var server_option = $MainMenuPanel/HiddenLogic/ServerOption +@onready var server_ip_input = $MainMenuPanel/HiddenLogic/ServerIPInput # Leaderboard Reference -@onready var leaderboard_btn = $MainMenuPanel/VBoxContainer/ButtonSection/LeaderboardBtn +@onready var leaderboard_btn = $MainMenuPanel/MainContainer/MarginContainer/TopRightPanel/LeaderboardBtn # UI References - Room List @onready var room_list_panel = $RoomListPanel @@ -99,10 +101,22 @@ var leaderboard_panel_instance: Control # Bot name tracking keyed by slot index to avoid re-generating on each update var _bot_names: Dictionary = {} +# ============================================================================= +# Chat System +# ============================================================================= +const GLOBAL_CHAT_ROOM := "global_lobby" +var _chat_channel = null +var _chat_messages: Array = [] + +@onready var chat_display: RichTextLabel = $MainMenuPanel/MainContainer/MarginContainer/LeftPanel/ChatPanel/MarginContainer/RichTextLabel +@onready var chat_input: LineEdit = $MainMenuPanel/MainContainer/MarginContainer/LeftPanel/HBoxContainer/ChatInput +@onready var chat_send_btn: Button = $MainMenuPanel/MainContainer/MarginContainer/LeftPanel/HBoxContainer/SendBtn + # Server Selection Controls (Now in tscn) # var server_option: OptionButton # var server_ip_input: LineEdit + func _ready(): # Start background music for Lobby MusicManager.start_music() @@ -121,21 +135,11 @@ func _ready(): # Initial UI update _on_profile_updated() - # Inject Tutorial button - var tutorial_btn = Button.new() - tutorial_btn.name = "TutorialBtn" - tutorial_btn.text = "PLAY TUTORIAL" - tutorial_btn.theme_type_variation = "FlatButton" - tutorial_btn.add_theme_font_size_override("font_size", 24) - tutorial_btn.pressed.connect(_on_tutorial_pressed) - var btn_section = main_menu_panel.get_node_or_null("VBoxContainer/ButtonSection") - if btn_section: - btn_section.add_child(tutorial_btn) - btn_section.move_child(tutorial_btn, 0) - # Connect button signals - Main Menu create_room_btn.pressed.connect(_on_create_room_pressed) browse_rooms_btn.pressed.connect(_on_browse_rooms_pressed) + if tutorial_btn: + tutorial_btn.pressed.connect(_on_tutorial_pressed) if main_menu_profile_btn: main_menu_profile_btn.pressed.connect(_on_profile_btn_pressed) if lobby_settings_btn: @@ -222,6 +226,12 @@ func _ready(): UserProfileManager.profile_loaded.connect(func(_p): _on_profile_updated()) UserProfileManager.profile_updated.connect(_on_profile_updated) + # Connect Chat UI + if chat_send_btn: + chat_send_btn.pressed.connect(_on_chat_send_pressed) + if chat_input: + chat_input.text_submitted.connect(func(_t): _on_chat_send_pressed()) + # Set initial title if already loaded _on_profile_updated() @@ -232,6 +242,11 @@ func _ready(): if not LobbyManager.disconnect_reason.is_empty(): connection_status.text = LobbyManager.disconnect_reason LobbyManager.disconnect_reason = "" + + # Try to join global chat if already connected + if NakamaManager.is_connected_to_nakama(): + _join_global_chat() + # ============================================================================= # Setup @@ -831,6 +846,7 @@ func _on_area_changed(area_name: String) -> void: func _on_connected_to_nakama() -> void: connection_status.text = "Connected to server" + _join_global_chat() func _on_connection_failed(error_message: String) -> void: connection_status.text = "Connection failed: %s" % error_message @@ -854,6 +870,16 @@ func _on_profile_updated() -> void: if main_title: main_title.text = display_name + if username_label: + username_label.text = display_name + + if main_menu_profile_btn: + var avatar_url = UserProfileManager.get_avatar_url() + if ResourceLoader.exists(avatar_url): + main_menu_profile_btn.icon = load(avatar_url) + main_menu_profile_btn.expand_icon = true + main_menu_profile_btn.text = "" + # Sync to LobbyManager LobbyManager.set_player_name(display_name) @@ -998,3 +1024,155 @@ func _apply_loadout_character() -> void: if idx != -1: LobbyManager.local_character_index = idx print("[Lobby] Loadout character applied: ", saved_char) + + +# ============================================================================= +# Global Chat System +# ============================================================================= + +func _join_global_chat() -> void: + """Join the persistent global lobby chat channel via Nakama socket.""" + # Already in the channel — don't rejoin + if _chat_channel != null: + return + + var socket = NakamaManager.socket + if not socket or not socket.is_connected_to_host(): + push_warning("[Chat] Socket not connected, skipping chat join.") + return + + # Join a persistent room channel named by our constant + var result = await socket.join_chat_async(GLOBAL_CHAT_ROOM, + NakamaSocket.ChannelType.Room, + true, # persistence = true (server stores history) + false) # hidden = false + + if result.is_exception(): + push_warning("[Chat] Failed to join global chat: " + result.get_exception().message) + return + + _chat_channel = result + print("[Chat] Joined global channel: ", _chat_channel.id) + + # Connect incoming message signal + if not socket.received_channel_message.is_connected(_on_chat_message_received): + socket.received_channel_message.connect(_on_chat_message_received) + + # Load history and render (Nakama sends recent messages on join via received_channel_message) + _refresh_chat_display() + +func _on_chat_message_received(message) -> void: + """Nakama socket signal: a message arrived on any channel.""" + # message is ApiChannelMessage — use direct property access, NOT .get() + if _chat_channel == null or message.channel_id != _chat_channel.id: + return + + # content is a String (JSON) — parse to extract our "msg" field + var text: String = "" + var parsed = JSON.parse_string(message.content) + if typeof(parsed) == TYPE_DICTIONARY: + text = parsed.get("msg", message.content) + else: + text = message.content + + # Sender: use username property directly (falls back to first 8 chars of sender_id) + var sender: String = message.username + if sender.is_empty(): + sender = message.sender_id.substr(0, 8) + + # Timestamp → HH:MM + var ts_str: String = _format_nakama_time(message.create_time) + + _chat_messages.append({ + "sender": sender, + "content": text, + "ts": ts_str, + "date": message.create_time.substr(0, 10) + }) + + _trim_old_messages() + _refresh_chat_display() + +func _on_chat_send_pressed() -> void: + """Send a message to the global chat channel.""" + if chat_input == null or chat_input.text.strip_edges().is_empty(): + return + + var text = chat_input.text.strip_edges() + chat_input.text = "" + chat_input.grab_focus() + + var socket = NakamaManager.socket + if not socket or _chat_channel == null: + # Offline fallback: show locally only + _inject_local_message(text) + return + + # Nakama GDScript SDK: write_chat_message_async takes a Dictionary, not a JSON string + var content := {"msg": text} + var result = await socket.write_chat_message_async(_chat_channel.id, content) + if result.is_exception(): + push_warning("[Chat] Failed to send message: " + result.get_exception().message) + # Still show it locally + _inject_local_message(text) + +func _inject_local_message(text: String) -> void: + """Display a message as the local player when offline/fallback.""" + var display_name = UserProfileManager.get_display_name("You") + var ts_str = _get_local_time_hhmm() + _chat_messages.append({ + "sender": display_name, + "content": text, + "ts": ts_str, + "date": Time.get_date_string_from_system() + }) + _refresh_chat_display() + +func _trim_old_messages() -> void: + """Remove messages from previous calendar days (daily clear).""" + var today: String = Time.get_date_string_from_system() + _chat_messages = _chat_messages.filter(func(m): return m.get("date", today) == today) + +func _refresh_chat_display() -> void: + """Re-render the RichTextLabel with all buffered messages.""" + if not chat_display: + return + + chat_display.clear() + for msg in _chat_messages: + var ts: String = msg.get("ts", "") + var sender: String = msg.get("sender", "?") + var text: String = msg.get("content", "") + + # Format: [dim][HH:MM] [/dim][b]Name:[/b] message + chat_display.append_text("[color=#888888][%s][/color] [b]%s:[/b] %s\n" % [ts, sender, text]) + + # Scroll to bottom + await get_tree().process_frame + chat_display.scroll_to_line(chat_display.get_line_count()) + +func _format_nakama_time(iso_str: String) -> String: + """Convert Nakama ISO timestamp '2026-04-14T10:30:00Z' → local 'HH:MM'.""" + # Parse the UTC time components + if iso_str.length() < 19: + return _get_local_time_hhmm() + var t_parts = iso_str.split("T") + if t_parts.size() < 2: + return _get_local_time_hhmm() + var time_part = t_parts[1].replace("Z", "").split(":") + if time_part.size() < 2: + return _get_local_time_hhmm() + # Use UTC hours/minutes directly (simple, avoids TZ complexity in Godot) + return "%s:%s" % [time_part[0], time_part[1]] + +func _get_local_time_hhmm() -> String: + var t = Time.get_time_dict_from_system() + return "%02d:%02d" % [t.hour, t.minute] + +func _leave_global_chat() -> void: + """Leave the chat channel cleanly.""" + var socket = NakamaManager.socket + if socket and _chat_channel: + socket.received_channel_message.disconnect(_on_chat_message_received) + await socket.leave_chat_async(_chat_channel.id) + _chat_channel = null diff --git a/scenes/lobby.tscn b/scenes/lobby.tscn index 7357e83..c0bf236 100644 --- a/scenes/lobby.tscn +++ b/scenes/lobby.tscn @@ -27,6 +27,7 @@ texture = ExtResource("3_q60fs") expand_mode = 2 [node name="Background2" type="TextureRect" parent="." unique_id=783164473] +visible = false layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -35,61 +36,229 @@ grow_horizontal = 2 grow_vertical = 2 texture = ExtResource("4_nqcc7") expand_mode = 2 +flip_h = true -[node name="MainMenuPanel" type="PanelContainer" parent="." unique_id=19191862] -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -221.0 -offset_top = -320.0 -offset_right = 219.0 -offset_bottom = 286.0 +[node name="MainMenuPanel" type="CanvasLayer" parent="." unique_id=296519838] + +[node name="MainContainer" type="Control" parent="MainMenuPanel" unique_id=1358259397] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -[node name="VBoxContainer" type="VBoxContainer" parent="MainMenuPanel" unique_id=590789898] -layout_mode = 2 -theme_override_constants/separation = 18 +[node name="MarginContainer" type="MarginContainer" parent="MainMenuPanel/MainContainer" unique_id=396129054] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 30 +theme_override_constants/margin_top = 30 +theme_override_constants/margin_right = 30 +theme_override_constants/margin_bottom = 30 -[node name="TitleContainer" type="VBoxContainer" parent="MainMenuPanel/VBoxContainer" unique_id=187177999] +[node name="LeftPanel" type="VBoxContainer" parent="MainMenuPanel/MainContainer/MarginContainer" unique_id=1627330537] layout_mode = 2 -theme_override_constants/separation = 4 +size_flags_horizontal = 0 -[node name="Title" type="Label" parent="MainMenuPanel/VBoxContainer/TitleContainer" unique_id=612210089] -layout_mode = 2 -theme_override_colors/font_color = Color(0.52, 0.52, 0.52, 1) -theme_override_font_sizes/font_size = 44 -text = "TEKTON DASH" -horizontal_alignment = 1 -vertical_alignment = 1 - -[node name="Subtitle" type="Label" parent="MainMenuPanel/VBoxContainer/TitleContainer" unique_id=670350843] -layout_mode = 2 -theme_override_colors/font_color = Color(0.71, 0.5751, 0.03550001, 1) -theme_override_font_sizes/font_size = 12 -text = "ARMAGEDDON VERSION" -horizontal_alignment = 1 - -[node name="Separator" type="HSeparator" parent="MainMenuPanel/VBoxContainer" unique_id=126990892] -layout_mode = 2 - -[node name="ServerSelectionSection" type="VBoxContainer" parent="MainMenuPanel/VBoxContainer" unique_id=748392101] -visible = false +[node name="ProfileBox" type="HBoxContainer" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel" unique_id=2112406804] layout_mode = 2 theme_override_constants/separation = 10 -[node name="ServerLabel" type="Label" parent="MainMenuPanel/VBoxContainer/ServerSelectionSection" unique_id=748392102] +[node name="ProfileBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/ProfileBox" unique_id=1711074288] +custom_minimum_size = Vector2(80, 80) layout_mode = 2 -theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1) -theme_override_font_sizes/font_size = 13 -text = "CONNECTION MODE" +text = "ICON" -[node name="ServerOption" type="OptionButton" parent="MainMenuPanel/VBoxContainer/ServerSelectionSection" unique_id=748392103] -custom_minimum_size = Vector2(0, 44) +[node name="VBoxContainer" type="VBoxContainer" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/ProfileBox" unique_id=1812465381] layout_mode = 2 +alignment = 1 + +[node name="Username" type="Label" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/ProfileBox/VBoxContainer" unique_id=499855830] +layout_mode = 2 +theme_override_font_sizes/font_size = 24 +text = "USERNAME" + +[node name="Subtitle" type="Label" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/ProfileBox/VBoxContainer" unique_id=2081013344] +layout_mode = 2 +theme_override_colors/font_color = Color(0.71, 0.575, 0.035, 1) +theme_override_font_sizes/font_size = 18 +text = "EU SERVER" + +[node name="Spacer" type="Control" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel" unique_id=2013644806] +custom_minimum_size = Vector2(0, 20) +layout_mode = 2 + +[node name="CurrenciesBox" type="VBoxContainer" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel" unique_id=1252464073] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="StarPanel" type="PanelContainer" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/CurrenciesBox" unique_id=1482932751] +custom_minimum_size = Vector2(160, 40) +layout_mode = 2 +size_flags_horizontal = 0 + +[node name="HBoxContainer" type="HBoxContainer" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/CurrenciesBox/StarPanel" unique_id=1113231570] +layout_mode = 2 +theme_override_constants/separation = 10 +alignment = 1 + +[node name="Icon" type="Label" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/CurrenciesBox/StarPanel/HBoxContainer" unique_id=1267791073] +layout_mode = 2 +text = "⭐" + +[node name="Label" type="Label" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/CurrenciesBox/StarPanel/HBoxContainer" unique_id=2117232678] +layout_mode = 2 +text = "23891" + +[node name="GoldPanel" type="PanelContainer" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/CurrenciesBox" unique_id=948555205] +custom_minimum_size = Vector2(160, 40) +layout_mode = 2 +size_flags_horizontal = 0 + +[node name="HBoxContainer" type="HBoxContainer" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/CurrenciesBox/GoldPanel" unique_id=788193079] +layout_mode = 2 +theme_override_constants/separation = 10 +alignment = 1 + +[node name="Icon" type="Label" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/CurrenciesBox/GoldPanel/HBoxContainer" unique_id=131964257] +layout_mode = 2 +text = "💰" + +[node name="Label" type="Label" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/CurrenciesBox/GoldPanel/HBoxContainer" unique_id=1973438143] +layout_mode = 2 +text = "3217" + +[node name="ChatSpacer" type="Control" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel" unique_id=1632908247] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="ChatPanel" type="PanelContainer" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel" unique_id=1582318454] +custom_minimum_size = Vector2(350, 200) +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/ChatPanel" unique_id=1866798800] +layout_mode = 2 +theme_override_constants/margin_left = 15 +theme_override_constants/margin_top = 15 +theme_override_constants/margin_right = 15 +theme_override_constants/margin_bottom = 15 + +[node name="RichTextLabel" type="RichTextLabel" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/ChatPanel/MarginContainer" unique_id=1563684167] +layout_mode = 2 +bbcode_enabled = true +scroll_following = true +text = "" + +[node name="HBoxContainer" type="HBoxContainer" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel" unique_id=976080441] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="QuitBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/HBoxContainer" unique_id=1499547561] +custom_minimum_size = Vector2(40, 40) +layout_mode = 2 +text = "<]" + +[node name="ChatInput" type="LineEdit" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/HBoxContainer" unique_id=1511957602] +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = " type to chat" + +[node name="SendBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/LeftPanel/HBoxContainer" unique_id=1337641536] +custom_minimum_size = Vector2(40, 40) +layout_mode = 2 +text = ">" + +[node name="TopRightPanel" type="HBoxContainer" parent="MainMenuPanel/MainContainer/MarginContainer" unique_id=377986819] +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 0 +theme_override_constants/separation = 15 + +[node name="CartBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/TopRightPanel" unique_id=2015877617] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +text = "🛒" + +[node name="TicketBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/TopRightPanel" unique_id=2142342229] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +text = "🎟" + +[node name="SocialBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/TopRightPanel" unique_id=23817931] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +text = "👥" + +[node name="LeaderboardBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/TopRightPanel" unique_id=1742815055] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +text = "🏆" + +[node name="SettingsBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/TopRightPanel" unique_id=439499917] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +text = "⚙" + +[node name="RightPanel" type="VBoxContainer" parent="MainMenuPanel/MainContainer/MarginContainer" unique_id=1806183556] +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +theme_override_constants/separation = 20 + +[node name="Banner1" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/RightPanel" unique_id=1918689135] +custom_minimum_size = Vector2(250, 120) +layout_mode = 2 +text = "Banner Slot" + +[node name="Banner2" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/RightPanel" unique_id=336081993] +custom_minimum_size = Vector2(250, 120) +layout_mode = 2 +text = "Banner Slot" + +[node name="Banner3" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/RightPanel" unique_id=942094893] +custom_minimum_size = Vector2(250, 120) +layout_mode = 2 +text = "Banner Slot" + +[node name="BottomRightPanel" type="HBoxContainer" parent="MainMenuPanel/MainContainer/MarginContainer" unique_id=923616751] +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 8 +theme_override_constants/separation = 15 + +[node name="TutorialBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/BottomRightPanel" unique_id=1468068637] +custom_minimum_size = Vector2(160, 60) +layout_mode = 2 +theme_override_font_sizes/font_size = 22 +text = "TUTORIAL" + +[node name="CreateRoomBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/BottomRightPanel" unique_id=1069893052] +custom_minimum_size = Vector2(160, 60) +layout_mode = 2 +theme_override_font_sizes/font_size = 22 +text = "HOST" + +[node name="BrowseRoomsBtn" type="Button" parent="MainMenuPanel/MainContainer/MarginContainer/BottomRightPanel" unique_id=1488596739] +custom_minimum_size = Vector2(160, 60) +layout_mode = 2 +theme_override_font_sizes/font_size = 22 +text = "BROWSE" + +[node name="HiddenLogic" type="Control" parent="MainMenuPanel" unique_id=1708805146] +visible = false +layout_mode = 3 +anchors_preset = 0 + +[node name="Title" type="Label" parent="MainMenuPanel/HiddenLogic" unique_id=586152975] +layout_mode = 0 +text = "TEKTON DASH" + +[node name="ServerOption" type="OptionButton" parent="MainMenuPanel/HiddenLogic" unique_id=837593831] +layout_mode = 0 selected = 3 item_count = 4 popup/item_0/text = "Nakama - Localhost (Testing)" @@ -101,56 +270,9 @@ popup/item_2/id = 2 popup/item_3/text = "Tekton Dash EU" popup/item_3/id = 3 -[node name="ServerIPInput" type="LineEdit" parent="MainMenuPanel/VBoxContainer/ServerSelectionSection" unique_id=748392104] -visible = false -custom_minimum_size = Vector2(0, 44) -layout_mode = 2 +[node name="ServerIPInput" type="LineEdit" parent="MainMenuPanel/HiddenLogic" unique_id=690927860] +layout_mode = 0 text = "127.0.0.1" -placeholder_text = "Enter Nakama Server IP..." - -[node name="ServerSeparator" type="HSeparator" parent="MainMenuPanel/VBoxContainer" unique_id=748392105] -visible = false -layout_mode = 2 - -[node name="ButtonSection" type="VBoxContainer" parent="MainMenuPanel/VBoxContainer" unique_id=793290187] -layout_mode = 2 -theme_override_constants/separation = 12 - -[node name="CreateRoomBtn" type="Button" parent="MainMenuPanel/VBoxContainer/ButtonSection" unique_id=750714] -custom_minimum_size = Vector2(0, 48) -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "CREATE ROOM" - -[node name="BrowseRoomsBtn" type="Button" parent="MainMenuPanel/VBoxContainer/ButtonSection" unique_id=2135081425] -custom_minimum_size = Vector2(0, 48) -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "BROWSE ROOMS" - -[node name="LeaderboardBtn" type="Button" parent="MainMenuPanel/VBoxContainer/ButtonSection" unique_id=216339260] -custom_minimum_size = Vector2(0, 48) -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "LEADERBOARD" - -[node name="SettingsBtn" type="Button" parent="MainMenuPanel/VBoxContainer/ButtonSection" unique_id=123456789] -custom_minimum_size = Vector2(0, 48) -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "SETTINGS" - -[node name="ProfileBtn" type="Button" parent="MainMenuPanel/VBoxContainer/ButtonSection" unique_id=1640960506] -custom_minimum_size = Vector2(0, 36) -layout_mode = 2 -theme_override_font_sizes/font_size = 14 -text = "PROFILE" - -[node name="QuitBtn" type="Button" parent="MainMenuPanel/VBoxContainer/ButtonSection" unique_id=123456780] -custom_minimum_size = Vector2(0, 36) -layout_mode = 2 -theme_override_font_sizes/font_size = 14 -text = "QUIT GAME" [node name="RoomListPanel" type="PanelContainer" parent="." unique_id=1782359692] visible = false @@ -1135,6 +1257,7 @@ offset_right = 40.0 offset_bottom = 40.0 [node name="StatusBar" type="PanelContainer" parent="." unique_id=1333732958] +visible = false layout_mode = 1 anchors_preset = 12 anchor_top = 1.0 diff --git a/scenes/player.tscn b/scenes/player.tscn index ee818ed..1203192 100644 --- a/scenes/player.tscn +++ b/scenes/player.tscn @@ -36,6 +36,7 @@ use_diagonal_movement = true [node name="Masbro" parent="." unique_id=1585899496 instance=ExtResource("2_mjsl8")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.485, 0) +visible = false [node name="Bob" parent="." unique_id=527434763 instance=ExtResource("2_3e0d5")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.484627, 0) @@ -47,7 +48,6 @@ visible = false [node name="Oldpop" parent="." unique_id=926683011 instance=ExtResource("5_alfd1")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.485, 0) -visible = false [node name="AnimationPlayer" type="AnimationPlayer" parent="." unique_id=1085499957] root_node = NodePath("../Masbro") @@ -55,6 +55,7 @@ libraries/animation-pack = ExtResource("6_5oq5w") [node name="CharacterPointer" type="MeshInstance3D" parent="." unique_id=1262762501] transform = Transform3D(0.3535534, 0, 0.35355335, 0, 0.5, 0, -0.35355335, 0, 0.3535534, 0, -0.468462, 0) +visible = false mesh = SubResource("TorusMesh_ur7pv") skeleton = NodePath("") diff --git a/scripts/managers/auth_manager.gd b/scripts/managers/auth_manager.gd index edaa803..61ac121 100644 --- a/scripts/managers/auth_manager.gd +++ b/scripts/managers/auth_manager.gd @@ -380,6 +380,8 @@ func logout() -> void: func _connect_socket() -> bool: if NakamaManager.socket and NakamaManager.socket.is_connected_to_host(): + if not multiplayer.has_multiplayer_peer() and NakamaManager.bridge: + multiplayer.set_multiplayer_peer(NakamaManager.bridge.multiplayer_peer) return true NakamaManager.socket = Nakama.create_socket_from(NakamaManager.client) diff --git a/scripts/managers/powerup_manager.gd b/scripts/managers/powerup_manager.gd index e5a3946..8b3e752 100644 --- a/scripts/managers/powerup_manager.gd +++ b/scripts/managers/powerup_manager.gd @@ -43,6 +43,8 @@ func initialize(p_player: Node3D, p_gridmap: Node): set_process(true) func _process(delta): + if not multiplayer.has_multiplayer_peer(): + return if not is_instance_valid(player) or not player.is_multiplayer_authority(): return diff --git a/scripts/managers/special_tiles_manager.gd b/scripts/managers/special_tiles_manager.gd index 5589033..5e53b0e 100644 --- a/scripts/managers/special_tiles_manager.gd +++ b/scripts/managers/special_tiles_manager.gd @@ -623,6 +623,8 @@ func _update_freeze_zones(delta: float): func _check_for_icy_floor(): # Every player checks if they are standing on an icy floor (item 15 on layer 2) # This ensures slow-mo works even if zones were cast by another player. + if not multiplayer.has_multiplayer_peer(): + return if not player.is_multiplayer_authority(): return diff --git a/scripts/nakama_manager.gd b/scripts/nakama_manager.gd index 26c8c33..f06d679 100644 --- a/scripts/nakama_manager.gd +++ b/scripts/nakama_manager.gd @@ -88,6 +88,8 @@ func _process(_delta): func connect_to_nakama_async(email: String = "", password: String = "") -> bool: if is_connected_to_nakama(): print("Already connected to Nakama.") + if not multiplayer.has_multiplayer_peer() and bridge: + multiplayer.set_multiplayer_peer(bridge.multiplayer_peer) emit_signal("connected_to_nakama") return true diff --git a/scripts/static_tekton_controller.gd b/scripts/static_tekton_controller.gd index b093214..5718202 100644 --- a/scripts/static_tekton_controller.gd +++ b/scripts/static_tekton_controller.gd @@ -35,6 +35,7 @@ func _start_timer(): timer.start() func _on_timer_timeout(): + if not multiplayer.has_multiplayer_peer(): return if not is_multiplayer_authority(): return if not tekton or not enhanced_gridmap: return diff --git a/scripts/tekton.gd b/scripts/tekton.gd index ffe8120..8e37334 100644 --- a/scripts/tekton.gd +++ b/scripts/tekton.gd @@ -202,7 +202,7 @@ func _process(delta): var mesh_cache: Array[MeshInstance3D] = [] var original_scales: Array[Vector3] = [] var prompt_container: Node3D -var _prompt_key_label: Label = null # cached ref for live refresh +var _prompt_key_label: Label3D = null # cached ref for live refresh @onready var SettingsManager = get_node_or_null("/root/SettingsManager") func _update_prompt_label(): @@ -212,6 +212,10 @@ func _update_prompt_label(): prompt_container.visible = false return + if not multiplayer.has_multiplayer_peer(): + prompt_container.visible = false + return + var authority_player = null var players = get_tree().get_nodes_in_group("Players") for p in players: