feat: update chat
This commit is contained in:
+200
-22
@@ -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
@@ -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
@@ -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("")
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user