feat: update chat

This commit is contained in:
2026-04-15 07:31:49 +08:00
parent a592eb1de0
commit 01661a56ba
9 changed files with 430 additions and 115 deletions
+200 -22
View File
@@ -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
+214 -91
View File
@@ -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
+2 -1
View File
@@ -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("")
+2
View File
@@ -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)
+2
View File
@@ -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
@@ -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
+2
View File
@@ -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
+1
View File
@@ -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
+5 -1
View File
@@ -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: