chore: release version 2.3.5 and refactor lobby
Bump export_presets.cfg version to 2.3.5. Update CHANGELOG_DRAFT.md. Refactor lobby.gd into LobbyChat, LobbyMainMenu, LobbyRoomList, LobbyRoom. Move Nakama config to environment variables in nakama_manager.gd. Derive auth_manager.gd encryption key from OS.get_unique_id().sha256_text(). Remove Steam email auth fallback. Require auth ticket. Make GachaManager.pull() async in gacha_panel.gd. Remove dummy wallet seeding. Add store_type to IAP payload. Validate IAP receipts server-side in economy.lua. Register gacha module in main.lua. Clean backend_service.gd stubs. Fix featured_banners type safety in gacha_manager.gd. Guards non-array responses. Move tiles_armagedon_a1.res to assets/models/meshes/. Fix import fallback_path.
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
class_name LobbyChat
|
||||
extends RefCounted
|
||||
|
||||
var lobby: Control
|
||||
|
||||
const GLOBAL_CHAT_ROOM := "social_global"
|
||||
var _chat_channel = null
|
||||
var _chat_messages: Array = []
|
||||
var _active_chat_context: String = "global"
|
||||
var _dm_tabs: Dictionary = {}
|
||||
var _dm_messages: Dictionary = {}
|
||||
|
||||
func _init(p_lobby: Control):
|
||||
lobby = p_lobby
|
||||
|
||||
if lobby.chat_send_btn:
|
||||
lobby.chat_send_btn.pressed.connect(_on_chat_send_pressed)
|
||||
if lobby.chat_input:
|
||||
lobby.chat_input.text_submitted.connect(func(_t): _on_chat_send_pressed())
|
||||
lobby.chat_input.text_changed.connect(_on_chat_input_changed)
|
||||
|
||||
_setup_friend_suggest_ui()
|
||||
|
||||
func join_global_chat() -> void:
|
||||
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
|
||||
|
||||
var result = await socket.join_chat_async(GLOBAL_CHAT_ROOM,
|
||||
NakamaSocket.ChannelType.Room,
|
||||
true, 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)
|
||||
|
||||
if not socket.received_channel_message.is_connected(_on_chat_message_received):
|
||||
socket.received_channel_message.connect(_on_chat_message_received)
|
||||
|
||||
_chat_messages.clear()
|
||||
var history_result = await NakamaManager.client.list_channel_messages_async(NakamaManager.session, _chat_channel.id, 50, false)
|
||||
if not history_result.is_exception() and history_result.messages:
|
||||
var msgs = history_result.messages.duplicate()
|
||||
msgs.reverse()
|
||||
for msg in msgs:
|
||||
_add_chat_message(msg, false)
|
||||
|
||||
_trim_old_messages()
|
||||
_refresh_chat_display()
|
||||
|
||||
func _on_chat_message_received(message) -> void:
|
||||
if _chat_channel == null or message.channel_id != _chat_channel.id:
|
||||
return
|
||||
if NakamaManager.session and message.sender_id == NakamaManager.session.user_id:
|
||||
return
|
||||
_add_chat_message(message, true)
|
||||
|
||||
func _add_chat_message(message, refresh_display: bool) -> void:
|
||||
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
|
||||
|
||||
var sender: String = message.username
|
||||
if sender.is_empty() and message.sender_id:
|
||||
sender = message.sender_id.substr(0, 8)
|
||||
elif sender.is_empty():
|
||||
sender = "Unknown"
|
||||
|
||||
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) if message.create_time else Time.get_date_string_from_system()
|
||||
})
|
||||
|
||||
if refresh_display:
|
||||
_trim_old_messages()
|
||||
_refresh_chat_display()
|
||||
|
||||
func _on_chat_send_pressed() -> void:
|
||||
if lobby.chat_input == null or lobby.chat_input.text.strip_edges().is_empty():
|
||||
return
|
||||
|
||||
var text = lobby.chat_input.text.strip_edges()
|
||||
lobby.chat_input.text = ""
|
||||
lobby.chat_input.grab_focus()
|
||||
|
||||
if lobby._friend_suggest_panel and lobby._friend_suggest_panel.visible:
|
||||
lobby._friend_suggest_panel.hide()
|
||||
|
||||
if text.begins_with("@"):
|
||||
var space_idx = text.find(" ")
|
||||
var target_username = ""
|
||||
var dm_message = ""
|
||||
|
||||
if space_idx > 0:
|
||||
target_username = text.substr(1, space_idx - 1)
|
||||
dm_message = text.substr(space_idx + 1).strip_edges()
|
||||
else:
|
||||
target_username = text.substr(1).strip_edges()
|
||||
|
||||
if not target_username.is_empty():
|
||||
var target_friend = null
|
||||
for f in FriendManager.friends:
|
||||
if f.get("username", "") == target_username:
|
||||
target_friend = f
|
||||
break
|
||||
|
||||
if target_friend:
|
||||
var user_id = target_friend.get("user_id", "")
|
||||
var username = target_friend.get("username", "")
|
||||
|
||||
_open_dm_tab(user_id, username)
|
||||
|
||||
if not dm_message.is_empty():
|
||||
_send_dm_message(user_id, dm_message)
|
||||
return
|
||||
elif text.begins_with("@"):
|
||||
_inject_local_message("User %s not found in friends." % target_username)
|
||||
return
|
||||
|
||||
if _active_chat_context != "global":
|
||||
if text == "/clear":
|
||||
_dm_messages[_active_chat_context] = []
|
||||
_refresh_chat_display()
|
||||
return
|
||||
_send_dm_message(_active_chat_context, text)
|
||||
return
|
||||
|
||||
if text == "/clear":
|
||||
var is_admin = await AdminManager._check_admin_status()
|
||||
if is_admin:
|
||||
_chat_messages.clear()
|
||||
_refresh_chat_display()
|
||||
if _chat_channel and NakamaManager.session and NakamaManager.client:
|
||||
var payload = JSON.stringify({"channel_id": _chat_channel.id})
|
||||
var rpc_result = await NakamaManager.client.rpc_async(NakamaManager.session, "admin_clear_global_chat", payload)
|
||||
if rpc_result.is_exception():
|
||||
push_warning("[Chat] admin_clear_global_chat RPC failed: " + rpc_result.get_exception().message)
|
||||
else:
|
||||
_inject_local_message("[SYSTEM] : Global chat cleared by admin.")
|
||||
else:
|
||||
_inject_local_message("[SYSTEM] : Unknown cmd, /clear only usable on DM between user.")
|
||||
return
|
||||
|
||||
_inject_local_message(text)
|
||||
|
||||
var socket = NakamaManager.socket
|
||||
if not socket or _chat_channel == null:
|
||||
return
|
||||
|
||||
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)
|
||||
|
||||
func _send_dm_message(user_id: String, text: String) -> void:
|
||||
var sent = await FriendManager.send_dm(user_id, text)
|
||||
if sent:
|
||||
if not _dm_messages.has(user_id):
|
||||
_dm_messages[user_id] = []
|
||||
_dm_messages[user_id].append({"sender": "You", "content": text, "ts": _get_local_time()})
|
||||
if _active_chat_context == user_id:
|
||||
_refresh_chat_display()
|
||||
|
||||
func on_lobby_dm_received(from_user_id: String, from_name: String, message: String) -> void:
|
||||
if not _dm_messages.has(from_user_id):
|
||||
_dm_messages[from_user_id] = []
|
||||
|
||||
_dm_messages[from_user_id].append({"sender": from_name, "content": message, "ts": _get_local_time()})
|
||||
|
||||
if not _dm_tabs.has(from_user_id):
|
||||
_create_dm_tab(from_user_id, from_name)
|
||||
|
||||
if _active_chat_context == from_user_id:
|
||||
_refresh_chat_display()
|
||||
|
||||
func _open_dm_tab(user_id: String, username: String) -> void:
|
||||
if not _dm_tabs.has(user_id):
|
||||
_create_dm_tab(user_id, username)
|
||||
_dm_messages[user_id] = []
|
||||
switch_chat_tab(user_id)
|
||||
|
||||
var history = await FriendManager.get_dm_history(user_id)
|
||||
var my_id = NakamaManager.session.user_id if NakamaManager.session else ""
|
||||
for entry in history:
|
||||
var is_self = entry.get("from") == my_id
|
||||
var sender_name = "You" if is_self else username
|
||||
var ts = _format_nakama_time(entry.get("create_time", ""))
|
||||
_dm_messages[user_id].append({"sender": sender_name, "content": entry.get("msg", ""), "ts": ts})
|
||||
|
||||
if _active_chat_context == user_id:
|
||||
_refresh_chat_display()
|
||||
else:
|
||||
switch_chat_tab(user_id)
|
||||
|
||||
func _create_dm_tab(user_id: String, username: String) -> void:
|
||||
var tabs_container = lobby.get_node_or_null("%ChatTabsContainer")
|
||||
var template = lobby.get_node_or_null("%DMTabTemplate")
|
||||
if not tabs_container or not template: return
|
||||
|
||||
var hbox = template.duplicate()
|
||||
hbox.visible = true
|
||||
|
||||
var btn = hbox.get_node("DMTabBtn")
|
||||
if btn:
|
||||
btn.text = username
|
||||
btn.pressed.connect(func(): switch_chat_tab(user_id))
|
||||
|
||||
var close_btn = hbox.get_node("DMTabCloseBtn")
|
||||
if close_btn:
|
||||
close_btn.pressed.connect(func(): _close_dm_tab(user_id))
|
||||
|
||||
tabs_container.add_child(hbox)
|
||||
_dm_tabs[user_id] = hbox
|
||||
|
||||
func _close_dm_tab(user_id: String) -> void:
|
||||
if _dm_tabs.has(user_id):
|
||||
var tab = _dm_tabs[user_id]
|
||||
tab.queue_free()
|
||||
_dm_tabs.erase(user_id)
|
||||
|
||||
if _active_chat_context == user_id:
|
||||
switch_chat_tab("global")
|
||||
|
||||
func switch_chat_tab(context_id: String) -> void:
|
||||
_active_chat_context = context_id
|
||||
_refresh_chat_display()
|
||||
|
||||
var tabs_container = lobby.get_node_or_null("%ChatTabsContainer")
|
||||
if tabs_container:
|
||||
var global_btn = lobby.get_node_or_null("%GlobalChatTabBtn")
|
||||
if global_btn:
|
||||
global_btn.modulate = Color(1.0, 1.0, 1.0) if context_id == "global" else Color(0.6, 0.6, 0.6)
|
||||
|
||||
for u_id in _dm_tabs:
|
||||
var tab_hbox = _dm_tabs[u_id]
|
||||
tab_hbox.modulate = Color(1.0, 1.0, 1.0) if context_id == u_id else Color(0.6, 0.6, 0.6)
|
||||
|
||||
func _setup_friend_suggest_ui() -> void:
|
||||
lobby._friend_suggest_panel = lobby.get_node_or_null("%FriendSuggestPanel")
|
||||
lobby._friend_suggest_list = lobby.get_node_or_null("%FriendSuggestList")
|
||||
if not lobby._friend_suggest_panel or not lobby._friend_suggest_list:
|
||||
push_warning("[Lobby] FriendSuggestPanel or FriendSuggestList not found in scene")
|
||||
return
|
||||
lobby._friend_suggest_panel.visible = false
|
||||
lobby._friend_suggest_list.item_activated.connect(_on_friend_suggest_activated)
|
||||
lobby._friend_suggest_list.item_selected.connect(_on_friend_suggest_activated)
|
||||
|
||||
func _on_chat_input_changed(new_text: String) -> void:
|
||||
if not lobby._friend_suggest_panel or not lobby._friend_suggest_list:
|
||||
return
|
||||
if new_text.begins_with("@") and not " " in new_text:
|
||||
var search = new_text.substr(1).to_lower()
|
||||
lobby._friend_suggest_list.clear()
|
||||
for f in FriendManager.friends:
|
||||
if f.get("state", -1) == FriendManager.STATE_FRIEND:
|
||||
var uname = f.get("username", "")
|
||||
if uname.to_lower().begins_with(search) or search.is_empty():
|
||||
lobby._friend_suggest_list.add_item(uname)
|
||||
|
||||
lobby._friend_suggest_panel.visible = lobby._friend_suggest_list.item_count > 0
|
||||
else:
|
||||
lobby._friend_suggest_panel.visible = false
|
||||
|
||||
func _on_friend_suggest_activated(index: int) -> void:
|
||||
if not lobby._friend_suggest_list:
|
||||
return
|
||||
var uname = lobby._friend_suggest_list.get_item_text(index)
|
||||
lobby.chat_input.text = "@%s " % uname
|
||||
lobby.chat_input.caret_column = lobby.chat_input.text.length()
|
||||
lobby.chat_input.grab_focus()
|
||||
lobby._friend_suggest_panel.visible = false
|
||||
|
||||
func _inject_local_message(text: String) -> void:
|
||||
var display_name = UserProfileManager.get_display_name("You")
|
||||
var ts_str = _get_local_time()
|
||||
_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:
|
||||
if _chat_messages.size() > 100:
|
||||
_chat_messages = _chat_messages.slice(-100)
|
||||
|
||||
func _refresh_chat_display() -> void:
|
||||
if not lobby.chat_display:
|
||||
return
|
||||
|
||||
lobby.chat_display.clear()
|
||||
|
||||
var messages_to_show = _chat_messages
|
||||
if _active_chat_context != "global":
|
||||
if _dm_messages.has(_active_chat_context):
|
||||
messages_to_show = _dm_messages[_active_chat_context]
|
||||
else:
|
||||
messages_to_show = []
|
||||
|
||||
for msg in messages_to_show:
|
||||
var ts: String = msg.get("ts", "")
|
||||
var sender: String = msg.get("sender", "?")
|
||||
var text: String = msg.get("content", "")
|
||||
|
||||
lobby.chat_display.append_text("[color=#888888][%s][/color] [b]%s:[/b] %s\n" % [ts, sender, text])
|
||||
|
||||
if lobby.chat_display:
|
||||
var scrollbar = lobby.chat_display.get_v_scroll_bar()
|
||||
if scrollbar:
|
||||
lobby.chat_display.scroll_to_line(lobby.chat_display.get_line_count())
|
||||
|
||||
func _format_nakama_time(time_str: String) -> String:
|
||||
if time_str.is_valid_int():
|
||||
var unix_time = time_str.to_int()
|
||||
var d = Time.get_date_dict_from_unix_time(unix_time)
|
||||
var t = Time.get_time_dict_from_unix_time(unix_time)
|
||||
return "%02d-%02d-%02d - %02d:%02d" % [d.day, d.month, d.year % 100, t.hour, t.minute]
|
||||
|
||||
if time_str.length() >= 19:
|
||||
var date_part = time_str.substr(0, 10).split("-")
|
||||
var time_part = time_str.substr(11, 5) # HH:MM
|
||||
if date_part.size() == 3:
|
||||
var year = date_part[0].substr(2, 2)
|
||||
var month = date_part[1]
|
||||
var day = date_part[2]
|
||||
return "%s-%s-%s - %s" % [day, month, year, time_part]
|
||||
|
||||
return _get_local_time()
|
||||
|
||||
func _get_local_time() -> String:
|
||||
var d = Time.get_date_dict_from_system()
|
||||
var t = Time.get_time_dict_from_system()
|
||||
return "%02d-%02d-%02d - %02d:%02d" % [d.day, d.month, d.year % 100, t.hour, t.minute]
|
||||
|
||||
func leave_global_chat() -> void:
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
uid://c322h8ysbs1vf
|
||||
@@ -0,0 +1,330 @@
|
||||
class_name LobbyMainMenu
|
||||
extends RefCounted
|
||||
|
||||
var lobby: Control
|
||||
|
||||
func _init(p_lobby: Control):
|
||||
lobby = p_lobby
|
||||
|
||||
if lobby.create_room_btn:
|
||||
lobby.create_room_btn.pressed.connect(on_create_room_pressed)
|
||||
if lobby.browse_rooms_btn:
|
||||
lobby.browse_rooms_btn.pressed.connect(on_browse_rooms_pressed)
|
||||
if lobby.tutorial_btn:
|
||||
lobby.tutorial_btn.pressed.connect(on_tutorial_pressed)
|
||||
if lobby.main_menu_profile_btn:
|
||||
lobby.main_menu_profile_btn.pressed.connect(on_profile_btn_pressed)
|
||||
if lobby.top_right_profile_btn:
|
||||
lobby.top_right_profile_btn.pressed.connect(on_profile_btn_pressed)
|
||||
if lobby.mailbox_btn:
|
||||
lobby.mailbox_btn.pressed.connect(on_mailbox_pressed)
|
||||
if lobby.lobby_settings_btn:
|
||||
lobby.lobby_settings_btn.pressed.connect(on_settings_pressed)
|
||||
if lobby.shop_btn:
|
||||
lobby.shop_btn.pressed.connect(on_shop_pressed)
|
||||
if lobby.banner1_btn:
|
||||
lobby.banner1_btn.disabled = false
|
||||
lobby.banner1_btn.text = "✨ Gacha"
|
||||
lobby.banner1_btn.pressed.connect(on_banner1_pressed)
|
||||
if lobby.leaderboard_btn:
|
||||
lobby.leaderboard_btn.pressed.connect(on_leaderboard_pressed)
|
||||
if lobby.ticket_btn:
|
||||
lobby.ticket_btn.pressed.connect(on_ticket_pressed)
|
||||
if lobby.quit_btn:
|
||||
lobby.quit_btn.pressed.connect(on_quit_pressed)
|
||||
|
||||
var social_btn = lobby.get_node_or_null("%SocialBtn")
|
||||
if social_btn:
|
||||
social_btn.pressed.connect(on_social_pressed)
|
||||
|
||||
if lobby.logout_btn:
|
||||
lobby.logout_btn.pressed.connect(on_logout_pressed)
|
||||
|
||||
# Connect Play Tab mode buttons
|
||||
var free_mode_btn = lobby.get_node_or_null("%FreeModeBtn")
|
||||
var stop_n_go_btn = lobby.get_node_or_null("%StopNGoBtn")
|
||||
if free_mode_btn:
|
||||
free_mode_btn.pressed.connect(func(): host_room("Freemode"))
|
||||
if stop_n_go_btn:
|
||||
stop_n_go_btn.pressed.connect(func(): host_room("Stop n Go"))
|
||||
|
||||
func on_tutorial_pressed() -> void:
|
||||
if AuthManager.is_guest:
|
||||
if LobbyManager.local_player_name.is_empty() or LobbyManager.local_player_name == "Player":
|
||||
LobbyManager.local_player_name = NameGenerator.generate_guest_name()
|
||||
else:
|
||||
LobbyManager.local_player_name = UserProfileManager.get_display_name()
|
||||
lobby.connection_status.text = "Starting Tutorial Scenario..."
|
||||
lobby._apply_loadout_character()
|
||||
LobbyManager.start_tutorial("Freemode")
|
||||
|
||||
await lobby.get_tree().process_frame
|
||||
await lobby.get_tree().process_frame
|
||||
LobbyManager.start_game(true)
|
||||
|
||||
func on_create_room_pressed() -> void:
|
||||
lobby._show_panel("room_list")
|
||||
var tabs = lobby.get_node_or_null("%RoomListTabs")
|
||||
if tabs:
|
||||
tabs.current_tab = 0
|
||||
if lobby.get_node_or_null("%PlayTabSideBtn"): lobby.get_node("%PlayTabSideBtn").button_pressed = true
|
||||
if lobby.get_node_or_null("%RoomTabSideBtn"): lobby.get_node("%RoomTabSideBtn").button_pressed = false
|
||||
lobby._sync_room_profile_card()
|
||||
|
||||
func host_room(game_mode: String) -> void:
|
||||
if AuthManager.is_guest:
|
||||
if LobbyManager.local_player_name.is_empty() or LobbyManager.local_player_name == "Player":
|
||||
LobbyManager.local_player_name = NameGenerator.generate_guest_name()
|
||||
else:
|
||||
LobbyManager.local_player_name = UserProfileManager.get_display_name()
|
||||
|
||||
if not game_mode.is_empty():
|
||||
LobbyManager.game_mode = game_mode
|
||||
LobbyManager._update_available_areas(game_mode)
|
||||
|
||||
lobby._apply_loadout_character()
|
||||
|
||||
var mode_prefix := "[%s] " % game_mode if not game_mode.is_empty() else ""
|
||||
|
||||
if LobbyManager.is_lan_mode:
|
||||
lobby.connection_status.text = "Starting LAN room..."
|
||||
var room_label := "%sLAN Room %d" % [mode_prefix, randi_range(100, 999)]
|
||||
var ok = await LobbyManager.create_room_lan(room_label)
|
||||
if not ok:
|
||||
lobby.connection_status.text = "Failed to start LAN room. Check port 7777."
|
||||
else:
|
||||
lobby.connection_status.text = "Creating Nakama room..."
|
||||
var room_label := "%sRoom %d" % [mode_prefix, randi_range(1000, 9999)]
|
||||
LobbyManager.create_room(room_label)
|
||||
|
||||
func on_browse_rooms_pressed() -> void:
|
||||
lobby._show_panel("room_list")
|
||||
var tabs = lobby.get_node_or_null("%RoomListTabs")
|
||||
if tabs:
|
||||
tabs.current_tab = 1
|
||||
if lobby.get_node_or_null("%PlayTabSideBtn"): lobby.get_node("%PlayTabSideBtn").button_pressed = false
|
||||
if lobby.get_node_or_null("%RoomTabSideBtn"): lobby.get_node("%RoomTabSideBtn").button_pressed = true
|
||||
lobby._sync_room_profile_card()
|
||||
|
||||
var match_id_label = lobby.get_node_or_null("%RoomListPanel/RoomListTabs/RoomTab/HBoxContainer/RightCol/MatchIdLabel")
|
||||
|
||||
if LobbyManager.is_lan_mode:
|
||||
lobby.connection_status.text = "LAN Mode - Enter Host IP to join"
|
||||
lobby.match_id_input.placeholder_text = "Enter Host IP (e.g. 192.168.1.10)..."
|
||||
if match_id_label: match_id_label.text = "DIRECT CONNECT (HOST IP)"
|
||||
lobby._on_refresh_pressed()
|
||||
else:
|
||||
lobby.connection_status.text = "Loading Nakama rooms..."
|
||||
lobby.match_id_input.placeholder_text = "Paste match ID here..."
|
||||
if match_id_label: match_id_label.text = "DIRECT CONNECT (MATCH ID)"
|
||||
LobbyManager.refresh_room_list()
|
||||
|
||||
func on_profile_btn_pressed() -> void:
|
||||
if not lobby.profile_panel_instance:
|
||||
var profile_panel_scene := load("res://scenes/ui/profile_panel.tscn")
|
||||
lobby.profile_panel_instance = profile_panel_scene.instantiate()
|
||||
lobby.profile_panel_instance.closed.connect(func():
|
||||
lobby.profile_panel_instance.hide()
|
||||
if lobby.main_menu_panel:
|
||||
lobby.main_menu_panel.show()
|
||||
)
|
||||
lobby.profile_panel_instance.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
lobby.add_child(lobby.profile_panel_instance)
|
||||
|
||||
if lobby.profile_panel_instance:
|
||||
if lobby.main_menu_panel:
|
||||
lobby.main_menu_panel.hide()
|
||||
lobby.profile_panel_instance.show_panel()
|
||||
|
||||
func on_mailbox_pressed() -> void:
|
||||
if not lobby._mailbox_panel_instance:
|
||||
var scene = load("res://scenes/ui/mailbox_panel.tscn")
|
||||
if scene:
|
||||
lobby._mailbox_panel_instance = scene.instantiate()
|
||||
lobby._mailbox_panel_instance.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
|
||||
var cl := CanvasLayer.new()
|
||||
cl.layer = 100
|
||||
cl.name = "MailboxCanvasLayer"
|
||||
lobby.add_child(cl)
|
||||
cl.add_child(lobby._mailbox_panel_instance)
|
||||
|
||||
if lobby._mailbox_panel_instance.has_signal("closed"):
|
||||
lobby._mailbox_panel_instance.closed.connect(func():
|
||||
lobby._mailbox_panel_instance.get_parent().queue_free()
|
||||
lobby._mailbox_panel_instance = null
|
||||
)
|
||||
|
||||
if lobby._mailbox_panel_instance:
|
||||
lobby._mailbox_panel_instance.show_panel()
|
||||
|
||||
func on_settings_pressed():
|
||||
var settings_menu = lobby.get_node_or_null("SettingsMenu")
|
||||
if not settings_menu:
|
||||
var scene = load("res://scenes/ui/settings_menu.tscn")
|
||||
if scene:
|
||||
settings_menu = scene.instantiate()
|
||||
settings_menu.name = "SettingsMenu"
|
||||
lobby.add_child(settings_menu)
|
||||
|
||||
var close_btn = settings_menu.get_node_or_null("PanelContainer/VBoxContainer/Header/CloseButton")
|
||||
if close_btn:
|
||||
close_btn.pressed.connect(func():
|
||||
restore_after_settings()
|
||||
)
|
||||
|
||||
if settings_menu:
|
||||
settings_menu.set_meta("from_lobby", lobby.lobby_panel.visible if lobby.lobby_panel else false)
|
||||
lobby.main_menu_panel.hide()
|
||||
if lobby.lobby_panel: lobby.lobby_panel.hide()
|
||||
settings_menu.open()
|
||||
|
||||
func restore_after_settings() -> void:
|
||||
var settings_menu = lobby.get_node_or_null("SettingsMenu")
|
||||
var from_lobby: bool = settings_menu.get_meta("from_lobby", false) if settings_menu else false
|
||||
if from_lobby:
|
||||
if lobby.lobby_panel: lobby.lobby_panel.show()
|
||||
else:
|
||||
if lobby.main_menu_panel: lobby.main_menu_panel.show()
|
||||
|
||||
func on_shop_pressed() -> void:
|
||||
if not NakamaManager.session:
|
||||
lobby.connection_status.text = "Must be logged in"
|
||||
return
|
||||
|
||||
if not lobby.shop_panel_instance:
|
||||
var shop_scene = load("res://scenes/ui/shop_panel.tscn")
|
||||
if shop_scene:
|
||||
lobby.shop_panel_instance = shop_scene.instantiate()
|
||||
lobby.add_child(lobby.shop_panel_instance)
|
||||
lobby.shop_panel_instance.closed.connect(func(): if lobby.main_menu_panel: lobby.main_menu_panel.show())
|
||||
|
||||
if lobby.shop_panel_instance:
|
||||
if lobby.main_menu_panel: lobby.main_menu_panel.hide()
|
||||
lobby.shop_panel_instance.show_panel()
|
||||
|
||||
func on_banner1_pressed() -> void:
|
||||
var gacha_scene = load("res://scenes/ui/gacha_panel.tscn")
|
||||
if not gacha_scene:
|
||||
lobby.connection_status.text = "Gacha panel not found"
|
||||
return
|
||||
var gacha = gacha_scene.instantiate()
|
||||
gacha.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
lobby.add_child(gacha)
|
||||
if lobby.main_menu_panel: lobby.main_menu_panel.hide()
|
||||
|
||||
if gacha.has_signal("closed"):
|
||||
gacha.closed.connect(func():
|
||||
gacha.queue_free()
|
||||
if lobby.main_menu_panel: lobby.main_menu_panel.show()
|
||||
)
|
||||
else:
|
||||
var back = gacha.get_node_or_null("%BackBtn")
|
||||
if back:
|
||||
back.pressed.connect(func():
|
||||
gacha.queue_free()
|
||||
if lobby.main_menu_panel: lobby.main_menu_panel.show()
|
||||
)
|
||||
|
||||
func on_leaderboard_pressed() -> void:
|
||||
if not lobby.leaderboard_panel_instance:
|
||||
var leaderboard_panel_scene := load("res://scenes/ui/leaderboard_panel.tscn")
|
||||
if leaderboard_panel_scene:
|
||||
lobby.leaderboard_panel_instance = leaderboard_panel_scene.instantiate()
|
||||
lobby.leaderboard_panel_instance.closed.connect(func():
|
||||
lobby.leaderboard_panel_instance.hide()
|
||||
if lobby.main_menu_panel:
|
||||
lobby.main_menu_panel.show()
|
||||
)
|
||||
lobby.leaderboard_panel_instance.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
lobby.add_child(lobby.leaderboard_panel_instance)
|
||||
|
||||
if lobby.leaderboard_panel_instance:
|
||||
if lobby.main_menu_panel:
|
||||
lobby.main_menu_panel.hide()
|
||||
lobby.leaderboard_panel_instance.show_panel()
|
||||
|
||||
func on_ticket_pressed() -> void:
|
||||
if not NakamaManager.session:
|
||||
lobby.connection_status.text = "Must be logged in"
|
||||
return
|
||||
|
||||
if not lobby.daily_reward_panel_instance:
|
||||
var scene = load("res://scenes/ui/daily_reward_panel.tscn")
|
||||
if scene:
|
||||
lobby.daily_reward_panel_instance = scene.instantiate()
|
||||
lobby.daily_reward_panel_instance.closed.connect(func():
|
||||
if lobby.main_menu_panel: lobby.main_menu_panel.show()
|
||||
)
|
||||
lobby.add_child(lobby.daily_reward_panel_instance)
|
||||
|
||||
if lobby.daily_reward_panel_instance:
|
||||
if lobby.main_menu_panel:
|
||||
lobby.main_menu_panel.hide()
|
||||
lobby.daily_reward_panel_instance.show_panel()
|
||||
|
||||
func on_social_pressed() -> void:
|
||||
if not lobby.social_panel_instance:
|
||||
var scene = load("res://scenes/ui/social_panel.tscn")
|
||||
if scene:
|
||||
lobby.social_panel_instance = scene.instantiate()
|
||||
lobby.social_panel_instance.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
|
||||
var cl := CanvasLayer.new()
|
||||
cl.layer = 100
|
||||
cl.name = "SocialCanvasLayer"
|
||||
lobby.add_child(cl)
|
||||
cl.add_child(lobby.social_panel_instance)
|
||||
|
||||
if lobby.social_panel_instance.has_signal("dm_requested"):
|
||||
lobby.social_panel_instance.dm_requested.connect(func(user_id, username):
|
||||
lobby.social_panel_instance.hide()
|
||||
lobby.chat._open_dm_tab(user_id, username)
|
||||
if lobby.main_menu_panel:
|
||||
var main_hbox = lobby.main_menu_panel.get_node_or_null("MainMargin/MainHBox")
|
||||
if main_hbox:
|
||||
var left_col = main_hbox.get_node_or_null("LeftCol")
|
||||
if left_col:
|
||||
for child in left_col.get_children():
|
||||
child.show()
|
||||
var right_col = main_hbox.get_node_or_null("RightCol")
|
||||
if right_col: right_col.show()
|
||||
)
|
||||
|
||||
if lobby.social_panel_instance.has_signal("closed"):
|
||||
lobby.social_panel_instance.closed.connect(func():
|
||||
lobby.social_panel_instance.hide()
|
||||
if lobby.main_menu_panel:
|
||||
var main_hbox = lobby.main_menu_panel.get_node_or_null("MainMargin/MainHBox")
|
||||
if main_hbox:
|
||||
var left_col = main_hbox.get_node_or_null("LeftCol")
|
||||
if left_col:
|
||||
for child in left_col.get_children():
|
||||
child.show()
|
||||
var right_col = main_hbox.get_node_or_null("RightCol")
|
||||
if right_col: right_col.show()
|
||||
)
|
||||
if lobby.social_panel_instance:
|
||||
if lobby.main_menu_panel:
|
||||
var main_hbox = lobby.main_menu_panel.get_node_or_null("MainMargin/MainHBox")
|
||||
if main_hbox:
|
||||
var left_col = main_hbox.get_node_or_null("LeftCol")
|
||||
if left_col:
|
||||
for child in left_col.get_children():
|
||||
if child.name not in ["ChatPanel", "HBoxContainer", "SpacerMiddle"]:
|
||||
child.hide()
|
||||
var right_col = main_hbox.get_node_or_null("RightCol")
|
||||
if right_col: right_col.hide()
|
||||
lobby.social_panel_instance.show()
|
||||
|
||||
func on_logout_pressed() -> void:
|
||||
AuthManager.logout()
|
||||
go_to_login()
|
||||
|
||||
func on_quit_pressed() -> void:
|
||||
print("[Lobby] Quitting game...")
|
||||
lobby.get_tree().quit()
|
||||
|
||||
func go_to_login() -> void:
|
||||
if lobby.get_tree():
|
||||
lobby.get_tree().change_scene_to_file("res://scenes/ui/login_screen.tscn")
|
||||
@@ -0,0 +1 @@
|
||||
uid://dppiqkwvp52s1
|
||||
@@ -0,0 +1,427 @@
|
||||
class_name LobbyRoom
|
||||
extends RefCounted
|
||||
|
||||
var lobby: Control
|
||||
var _bot_names: Dictionary = {}
|
||||
|
||||
func _init(p_lobby: Control):
|
||||
lobby = p_lobby
|
||||
|
||||
if lobby.copy_id_btn:
|
||||
lobby.copy_id_btn.pressed.connect(_on_copy_id_pressed)
|
||||
if lobby.duration_option:
|
||||
lobby.duration_option.item_selected.connect(_on_duration_selected)
|
||||
if lobby.random_spawn_check:
|
||||
lobby.random_spawn_check.toggled.connect(_on_random_spawn_toggled)
|
||||
if lobby.enable_timer_check:
|
||||
lobby.enable_timer_check.toggled.connect(_on_enable_timer_toggled)
|
||||
if lobby.area_left_btn:
|
||||
lobby.area_left_btn.pressed.connect(func(): LobbyManager.cycle_area(-1))
|
||||
if lobby.area_right_btn:
|
||||
lobby.area_right_btn.pressed.connect(func(): LobbyManager.cycle_area(1))
|
||||
if lobby.leave_btn:
|
||||
lobby.leave_btn.pressed.connect(_on_leave_pressed)
|
||||
if lobby.ready_btn:
|
||||
lobby.ready_btn.toggled.connect(_on_ready_toggled)
|
||||
if lobby.start_game_btn:
|
||||
lobby.start_game_btn.pressed.connect(_on_start_game_pressed)
|
||||
if lobby.scarcity_option:
|
||||
lobby.scarcity_option.item_selected.connect(_on_scarcity_selected)
|
||||
if lobby.game_mode_option:
|
||||
lobby.game_mode_option.item_selected.connect(_on_game_mode_selected)
|
||||
|
||||
# Connect LobbyManager signals
|
||||
LobbyManager.room_joined.connect(_on_room_joined)
|
||||
LobbyManager.room_left.connect(_on_room_left)
|
||||
LobbyManager.host_disconnected.connect(_on_host_disconnected)
|
||||
LobbyManager.player_joined.connect(_on_player_joined)
|
||||
LobbyManager.player_left.connect(_on_player_left)
|
||||
LobbyManager.ready_state_changed.connect(_on_ready_state_changed)
|
||||
LobbyManager.all_players_ready.connect(_on_all_players_ready)
|
||||
LobbyManager.game_starting.connect(_on_game_starting)
|
||||
LobbyManager.match_duration_changed.connect(_on_match_duration_changed)
|
||||
LobbyManager.randomize_spawn_changed.connect(_on_randomize_spawn_changed)
|
||||
LobbyManager.enable_cycle_timer_changed.connect(_on_enable_cycle_timer_changed)
|
||||
LobbyManager.character_changed.connect(_on_character_changed)
|
||||
LobbyManager.area_changed.connect(_on_area_changed)
|
||||
LobbyManager.scarcity_mode_changed.connect(_on_scarcity_mode_changed)
|
||||
LobbyManager.game_mode_changed.connect(_on_game_mode_changed)
|
||||
LobbyManager.player_list_changed.connect(_update_player_slots)
|
||||
|
||||
LobbyManager.sng_go_duration_changed.connect(_on_sng_update)
|
||||
LobbyManager.sng_stop_duration_changed.connect(_on_sng_update)
|
||||
LobbyManager.sng_required_goals_changed.connect(_on_sng_update)
|
||||
LobbyManager.doors_swap_time_changed.connect(_on_doors_update)
|
||||
LobbyManager.doors_refresh_time_changed.connect(_on_doors_update)
|
||||
LobbyManager.doors_required_goals_changed.connect(_on_doors_update)
|
||||
|
||||
FriendManager.lobby_invite_received.connect(_on_lobby_invite_received)
|
||||
|
||||
# =============================================================================
|
||||
# Button Handlers
|
||||
# =============================================================================
|
||||
|
||||
func _on_ready_toggled(is_ready: bool) -> void:
|
||||
LobbyManager.set_ready(is_ready)
|
||||
if lobby.ready_btn:
|
||||
lobby.ready_btn.text = "READY ✓" if is_ready else "READY"
|
||||
|
||||
func _on_start_game_pressed() -> void:
|
||||
LobbyManager.start_game()
|
||||
|
||||
func _on_leave_pressed() -> void:
|
||||
LobbyManager.leave_room()
|
||||
lobby._show_panel("main_menu")
|
||||
if lobby.ready_btn:
|
||||
lobby.ready_btn.button_pressed = false
|
||||
lobby.ready_btn.text = "READY"
|
||||
for key in _bot_names.keys():
|
||||
NameGenerator.release_bot_name(_bot_names[key])
|
||||
_bot_names.clear()
|
||||
|
||||
func _on_copy_id_pressed() -> void:
|
||||
DisplayServer.clipboard_set(lobby.current_match_id)
|
||||
if lobby.status_label:
|
||||
lobby.status_label.text = "Match ID copied!"
|
||||
|
||||
func _on_duration_selected(index: int) -> void:
|
||||
if not LobbyManager.is_host:
|
||||
return
|
||||
var durations = [60, 120, 180, 300, 600]
|
||||
if index >= 0 and index < durations.size():
|
||||
LobbyManager.set_match_duration(durations[index])
|
||||
|
||||
func _on_random_spawn_toggled(toggled_on):
|
||||
LobbyManager.set_randomize_spawn(toggled_on)
|
||||
|
||||
func _on_enable_timer_toggled(toggled_on):
|
||||
LobbyManager.set_enable_cycle_timer(toggled_on)
|
||||
|
||||
func _on_scarcity_selected(index: int) -> void:
|
||||
if not LobbyManager.is_host: return
|
||||
var mode = lobby.scarcity_option.get_item_text(index)
|
||||
LobbyManager.set_scarcity_mode(mode)
|
||||
|
||||
func _on_scarcity_mode_changed(mode: String) -> void:
|
||||
if lobby.scarcity_option:
|
||||
for i in range(lobby.scarcity_option.item_count):
|
||||
if lobby.scarcity_option.get_item_text(i) == mode:
|
||||
lobby.scarcity_option.selected = i
|
||||
break
|
||||
if lobby.scarcity_label:
|
||||
lobby.scarcity_label.text = mode
|
||||
|
||||
func _on_game_mode_selected(index: int) -> void:
|
||||
if not LobbyManager.is_host: return
|
||||
var mode = lobby.game_mode_option.get_item_text(index)
|
||||
LobbyManager.set_game_mode(mode)
|
||||
|
||||
func _on_game_mode_changed(mode: String) -> void:
|
||||
if lobby.game_mode_option:
|
||||
for i in range(lobby.game_mode_option.item_count):
|
||||
if lobby.game_mode_option.get_item_text(i) == mode:
|
||||
lobby.game_mode_option.selected = i
|
||||
break
|
||||
if lobby.game_mode_text_label:
|
||||
lobby.game_mode_text_label.text = mode
|
||||
lobby._update_settings_visibility()
|
||||
|
||||
func _on_sng_update(_val: int = 0) -> void:
|
||||
if not lobby.sng_go_option: return
|
||||
var go_idx = [10, 15, 25].find(LobbyManager.sng_go_duration)
|
||||
if go_idx != -1: lobby.sng_go_option.selected = go_idx
|
||||
var stop_idx = [3, 4, 5].find(LobbyManager.sng_stop_duration)
|
||||
if stop_idx != -1: lobby.sng_stop_option.selected = stop_idx
|
||||
var goals_idx = [5, 8, 12].find(LobbyManager.sng_required_goals)
|
||||
if goals_idx != -1: lobby.sng_goals_option.selected = goals_idx
|
||||
|
||||
func _on_doors_update(_val: int = 0) -> void:
|
||||
if not lobby.doors_swap_option: return
|
||||
var swap_idx = [10, 15, 30].find(LobbyManager.doors_swap_time)
|
||||
if swap_idx != -1: lobby.doors_swap_option.selected = swap_idx
|
||||
var refresh_idx = [15, 25, 40].find(LobbyManager.doors_refresh_time)
|
||||
if refresh_idx != -1: lobby.doors_refresh_option.selected = refresh_idx
|
||||
var goals_idx = [5, 8, 12].find(LobbyManager.doors_required_goals)
|
||||
if goals_idx != -1: lobby.doors_goals_option.selected = goals_idx
|
||||
|
||||
# =============================================================================
|
||||
# LobbyManager Signal Handlers
|
||||
# =============================================================================
|
||||
|
||||
func _on_room_joined(room_data: Dictionary) -> void:
|
||||
lobby._show_panel("lobby")
|
||||
lobby.current_match_id = room_data.get("match_id", "")
|
||||
if lobby.match_id_display:
|
||||
lobby.match_id_display.text = "ID: %s" % _truncate_id(lobby.current_match_id)
|
||||
|
||||
var is_host = LobbyManager.is_host
|
||||
if lobby.host_banner: lobby.host_banner.visible = is_host
|
||||
if lobby.start_game_btn: lobby.start_game_btn.visible = is_host
|
||||
|
||||
lobby._update_settings_visibility()
|
||||
|
||||
_on_match_duration_changed(LobbyManager.match_duration)
|
||||
_on_randomize_spawn_changed(LobbyManager.randomize_spawn)
|
||||
_on_enable_cycle_timer_changed(LobbyManager.enable_cycle_timer)
|
||||
_on_scarcity_mode_changed(LobbyManager.scarcity_mode)
|
||||
_on_sng_update()
|
||||
_on_doors_update()
|
||||
|
||||
if lobby.area_left_btn: lobby.area_left_btn.disabled = not is_host
|
||||
if lobby.area_right_btn: lobby.area_right_btn.disabled = not is_host
|
||||
if lobby.area_name_label: lobby.area_name_label.text = LobbyManager.get_selected_area()
|
||||
|
||||
if lobby.game_mode_option: lobby.game_mode_option.visible = is_host
|
||||
if lobby.game_mode_text_label: lobby.game_mode_text_label.visible = not is_host
|
||||
_on_game_mode_changed(LobbyManager.game_mode)
|
||||
|
||||
_update_player_slots()
|
||||
if lobby.connection_status: lobby.connection_status.text = "Connected to room"
|
||||
|
||||
func _on_room_left() -> void:
|
||||
lobby._show_panel("main_menu")
|
||||
if lobby.connection_status: lobby.connection_status.text = "Left room"
|
||||
for key in _bot_names.keys():
|
||||
NameGenerator.release_bot_name(_bot_names[key])
|
||||
_bot_names.clear()
|
||||
|
||||
func _on_host_disconnected() -> void:
|
||||
if lobby.connection_status: lobby.connection_status.text = "Host disconnected. Returning to menu..."
|
||||
lobby._show_panel("main_menu")
|
||||
|
||||
func _on_player_joined(player_data: Dictionary) -> void:
|
||||
_update_player_slots()
|
||||
if lobby.status_label: lobby.status_label.text = "%s joined!" % player_data.get("name", "Player")
|
||||
|
||||
func _on_player_left(_player_id: int) -> void:
|
||||
_update_player_slots()
|
||||
if lobby.status_label: lobby.status_label.text = "A player left"
|
||||
|
||||
func _on_ready_state_changed(_player_id: int, _is_ready: bool) -> void:
|
||||
_update_player_slots()
|
||||
_update_status()
|
||||
|
||||
func _on_all_players_ready() -> void:
|
||||
if LobbyManager.is_host:
|
||||
if lobby.start_game_btn: lobby.start_game_btn.disabled = false
|
||||
if lobby.status_label: lobby.status_label.text = "All ready! Start the match!"
|
||||
else:
|
||||
if lobby.status_label: lobby.status_label.text = "All ready! Waiting for host..."
|
||||
|
||||
func _on_game_starting() -> void:
|
||||
if lobby.connection_status: lobby.connection_status.text = "Starting game..."
|
||||
var loading_screen_scene = load("res://scenes/loading_screen/loading_screen.tscn")
|
||||
if loading_screen_scene:
|
||||
var loading_screen = loading_screen_scene.instantiate()
|
||||
lobby.get_tree().root.add_child(loading_screen)
|
||||
loading_screen.load_level("res://scenes/main.tscn")
|
||||
else:
|
||||
lobby.get_tree().change_scene_to_file("res://scenes/main.tscn")
|
||||
|
||||
func _on_match_duration_changed(duration_seconds: int) -> void:
|
||||
if not LobbyManager.is_host:
|
||||
var duration_text: String
|
||||
match duration_seconds:
|
||||
60: duration_text = "1 min"
|
||||
120: duration_text = "2 min"
|
||||
180: duration_text = "3 min"
|
||||
300: duration_text = "5 min"
|
||||
600: duration_text = "10 min"
|
||||
_: duration_text = "%d sec" % duration_seconds
|
||||
if lobby.duration_text_label: lobby.duration_text_label.text = duration_text
|
||||
|
||||
func _on_randomize_spawn_changed(enabled: bool) -> void:
|
||||
if lobby.random_spawn_check:
|
||||
lobby.random_spawn_check.set_pressed_no_signal(enabled)
|
||||
if lobby.random_spawn_label:
|
||||
lobby.random_spawn_label.text = "Random \u2713" if enabled else "Random \u2717"
|
||||
|
||||
func _on_enable_cycle_timer_changed(enabled: bool) -> void:
|
||||
if lobby.enable_timer_check:
|
||||
lobby.enable_timer_check.set_pressed_no_signal(enabled)
|
||||
if lobby.enable_timer_label:
|
||||
lobby.enable_timer_label.text = "Timer \u2713" if enabled else "Timer \u2717"
|
||||
|
||||
func _on_character_changed(_player_id: int, _character_name: String) -> void:
|
||||
_update_player_slots()
|
||||
|
||||
func _on_area_changed(area_name: String) -> void:
|
||||
if lobby.area_name_label: lobby.area_name_label.text = area_name
|
||||
|
||||
func _update_player_slots() -> void:
|
||||
if not lobby.multiplayer.has_multiplayer_peer():
|
||||
return
|
||||
|
||||
var players = LobbyManager.get_players()
|
||||
var my_id = lobby.multiplayer.get_unique_id()
|
||||
|
||||
for i in range(lobby.player_slots.size()):
|
||||
var slot = lobby.player_slots[i]
|
||||
var slot_num = i + 1
|
||||
|
||||
if i < players.size():
|
||||
var player = players[i]
|
||||
slot.visible = true
|
||||
|
||||
var name_label = slot.get_node_or_null("PlayerName%d" % slot_num)
|
||||
if name_label:
|
||||
var display_name = player.get("name", "Player %d" % slot_num)
|
||||
if player.get("id") == 1:
|
||||
display_name += " (Host)"
|
||||
name_label.text = display_name
|
||||
|
||||
var char_preview = slot.get_node_or_null("CharacterPreview%d" % slot_num)
|
||||
var char_name = player.get("character", "Bob")
|
||||
if char_preview and lobby.character_textures.has(char_name):
|
||||
char_preview.texture = lobby.character_textures[char_name]
|
||||
|
||||
var is_local_player = player.get("id") == my_id
|
||||
|
||||
var char_name_in_nav = slot.get_node_or_null("CharacterNav%d/CharacterName%d" % [slot_num, slot_num])
|
||||
if char_name_in_nav:
|
||||
char_name_in_nav.text = char_name
|
||||
|
||||
var char_name_label = slot.get_node_or_null("CharacterNameLabel%d" % slot_num)
|
||||
if char_name_label:
|
||||
char_name_label.text = char_name
|
||||
char_name_label.visible = not is_local_player
|
||||
|
||||
var char_nav = slot.get_node_or_null("CharacterNav%d" % slot_num)
|
||||
if char_nav:
|
||||
char_nav.visible = is_local_player
|
||||
|
||||
var ready_label = slot.get_node_or_null("ReadyStatus%d" % slot_num)
|
||||
if ready_label:
|
||||
var is_ready = player.get("is_ready", false)
|
||||
ready_label.text = "READY ✓" if is_ready else "NOT READY"
|
||||
ready_label.add_theme_color_override("font_color",
|
||||
Color(0.4, 0.8, 0.4) if is_ready else Color(0.6, 0.6, 0.6))
|
||||
|
||||
var player_nakama_id: String = player.get("nakama_id", "")
|
||||
var my_nakama_id: String = NakamaManager.session.user_id if NakamaManager.session else ""
|
||||
var add_friend_btn: Button = slot.get_node_or_null("AddFriendBtn%d" % slot_num)
|
||||
if add_friend_btn:
|
||||
if player_nakama_id.is_empty() or player_nakama_id == my_nakama_id:
|
||||
add_friend_btn.visible = false
|
||||
else:
|
||||
add_friend_btn.visible = true
|
||||
if FriendManager.is_friend(player_nakama_id):
|
||||
add_friend_btn.text = "Friend ✓"
|
||||
add_friend_btn.disabled = true
|
||||
else:
|
||||
add_friend_btn.text = "+ Friend"
|
||||
add_friend_btn.disabled = false
|
||||
if not add_friend_btn.pressed.is_connected(func(): _on_add_friend_pressed(player_nakama_id)):
|
||||
add_friend_btn.pressed.connect(func(): _on_add_friend_pressed(player_nakama_id))
|
||||
else:
|
||||
slot.visible = true
|
||||
if not _bot_names.has(i):
|
||||
_bot_names[i] = NameGenerator.generate_bot_name()
|
||||
var bot_display_name: String = _bot_names[i]
|
||||
|
||||
var name_label = slot.get_node_or_null("PlayerName%d" % slot_num)
|
||||
if name_label:
|
||||
name_label.text = "🤖 " + bot_display_name
|
||||
|
||||
var char_preview = slot.get_node_or_null("CharacterPreview%d" % slot_num)
|
||||
var bot_characters = ["Copper", "Dabro", "Gatot", "Pip"]
|
||||
var bot_char = bot_characters[(i) % bot_characters.size()]
|
||||
if char_preview and lobby.character_textures.has(bot_char):
|
||||
char_preview.texture = lobby.character_textures[bot_char]
|
||||
|
||||
var char_nav = slot.get_node_or_null("CharacterNav%d" % slot_num)
|
||||
if char_nav:
|
||||
char_nav.visible = false
|
||||
|
||||
var char_name_label = slot.get_node_or_null("CharacterNameLabel%d" % slot_num)
|
||||
if char_name_label:
|
||||
char_name_label.text = bot_char
|
||||
char_name_label.visible = true
|
||||
|
||||
var ready_label = slot.get_node_or_null("ReadyStatus%d" % slot_num)
|
||||
if ready_label:
|
||||
ready_label.text = "WAITING..."
|
||||
ready_label.add_theme_color_override("font_color", Color(0.5, 0.5, 0.7))
|
||||
|
||||
var add_friend_btn: Button = slot.get_node_or_null("AddFriendBtn%d" % slot_num)
|
||||
if add_friend_btn:
|
||||
add_friend_btn.visible = false
|
||||
|
||||
_update_status()
|
||||
|
||||
func _update_status() -> void:
|
||||
var players = LobbyManager.get_players()
|
||||
var ready_count = 0
|
||||
for player in players:
|
||||
if player.get("is_ready", false):
|
||||
ready_count += 1
|
||||
|
||||
if lobby.status_label:
|
||||
lobby.status_label.text = "Ready: %d/%d" % [ready_count, players.size()]
|
||||
|
||||
if LobbyManager.is_host:
|
||||
if lobby.start_game_btn: lobby.start_game_btn.disabled = not LobbyManager.is_all_ready()
|
||||
|
||||
func _truncate_id(id: String) -> String:
|
||||
if id.length() > 16:
|
||||
return id.substr(0, 8) + "..." + id.substr(-4)
|
||||
return id
|
||||
|
||||
# =============================================================================
|
||||
# Social / Friend Functions
|
||||
# =============================================================================
|
||||
|
||||
func _on_add_friend_pressed(nakama_id: String) -> void:
|
||||
var ok = await FriendManager.add_friend_by_id(nakama_id)
|
||||
if ok:
|
||||
_update_player_slots()
|
||||
|
||||
func on_invite_friends_pressed() -> void:
|
||||
var match_id = lobby.current_match_id
|
||||
if match_id.is_empty():
|
||||
return
|
||||
var friends = FriendManager.get_mutual_friends()
|
||||
var scene = load("res://scenes/ui/invite_friends_dialog.tscn") as PackedScene
|
||||
if not scene:
|
||||
return
|
||||
var dialog = scene.instantiate()
|
||||
lobby.add_child(dialog)
|
||||
dialog.open(friends, match_id)
|
||||
dialog.closed.connect(dialog.queue_free)
|
||||
|
||||
func _on_lobby_invite_received(from_user_id: String, from_name: String, match_id: String) -> void:
|
||||
if lobby.get_tree().current_scene.scene_file_path != "res://scenes/lobby.tscn":
|
||||
return
|
||||
if lobby.lobby_panel and lobby.lobby_panel.visible:
|
||||
return
|
||||
|
||||
if lobby._invite_popup:
|
||||
lobby._invite_popup.queue_free()
|
||||
|
||||
lobby._pending_invite_match_id = match_id
|
||||
var scene = load("res://scenes/ui/lobby_invite_popup.tscn") as PackedScene
|
||||
if scene:
|
||||
lobby._invite_popup = scene.instantiate()
|
||||
lobby.add_child(lobby._invite_popup)
|
||||
lobby._invite_popup.setup(from_name)
|
||||
lobby._invite_popup.accepted.connect(_on_invite_accepted)
|
||||
lobby._invite_popup.declined.connect(func(): lobby._invite_popup.queue_free())
|
||||
lobby._invite_popup.popup_centered()
|
||||
else:
|
||||
var dlg := AcceptDialog.new()
|
||||
dlg.title = "Lobby Invitation"
|
||||
dlg.dialog_text = "%s invited you!\nJoin?" % from_name
|
||||
dlg.ok_button_text = "Join"
|
||||
dlg.add_cancel_button("Decline")
|
||||
lobby.add_child(dlg)
|
||||
dlg.confirmed.connect(_on_invite_accepted)
|
||||
dlg.canceled.connect(dlg.queue_free)
|
||||
dlg.popup_centered()
|
||||
lobby._invite_popup = dlg
|
||||
|
||||
func _on_invite_accepted() -> void:
|
||||
if not lobby._pending_invite_match_id.is_empty():
|
||||
LobbyManager.join_room(lobby._pending_invite_match_id)
|
||||
if lobby._invite_popup:
|
||||
lobby._invite_popup.queue_free()
|
||||
lobby._pending_invite_match_id = ""
|
||||
@@ -0,0 +1 @@
|
||||
uid://bfhxueb82oi57
|
||||
@@ -0,0 +1,155 @@
|
||||
class_name LobbyRoomList
|
||||
extends RefCounted
|
||||
|
||||
var lobby: Control
|
||||
|
||||
func _init(p_lobby: Control):
|
||||
lobby = p_lobby
|
||||
|
||||
if lobby.refresh_btn:
|
||||
lobby.refresh_btn.pressed.connect(_on_refresh_pressed)
|
||||
if lobby.join_btn:
|
||||
lobby.join_btn.pressed.connect(_on_join_pressed)
|
||||
if lobby.back_btn:
|
||||
lobby.back_btn.pressed.connect(_on_back_pressed)
|
||||
|
||||
LobbyManager.room_list_updated.connect(on_room_list_updated)
|
||||
|
||||
func _on_refresh_pressed() -> void:
|
||||
if lobby.connection_status: lobby.connection_status.text = "Refreshing..."
|
||||
if lobby.room_list: lobby.room_list.clear()
|
||||
LobbyManager.refresh_room_list()
|
||||
|
||||
func _on_room_selected(index: int) -> void:
|
||||
if index < LobbyManager.available_rooms.size():
|
||||
var room = LobbyManager.available_rooms[index]
|
||||
if LobbyManager.is_lan_mode:
|
||||
var ip = room.get("ip", "")
|
||||
if not ip.is_empty() and lobby.match_id_input:
|
||||
lobby.match_id_input.text = ip
|
||||
else:
|
||||
var mid = room.get("match_id", "")
|
||||
if not mid.is_empty() and lobby.match_id_input:
|
||||
lobby.match_id_input.text = mid
|
||||
|
||||
func _on_room_activated(index: int) -> void:
|
||||
_on_room_selected(index)
|
||||
_on_join_pressed()
|
||||
|
||||
func _on_join_pressed() -> void:
|
||||
if not lobby.match_id_input: return
|
||||
var match_id = lobby.match_id_input.text.strip_edges()
|
||||
|
||||
if match_id.is_empty() and not LobbyManager.is_lan_mode:
|
||||
var selected_items = [] if not lobby.room_list else lobby.room_list.get_selected_items()
|
||||
if selected_items.size() == 0:
|
||||
if lobby.connection_status: lobby.connection_status.text = "Please select a room or enter Match ID"
|
||||
return
|
||||
|
||||
var selected_idx = selected_items[0]
|
||||
if selected_idx < LobbyManager.available_rooms.size():
|
||||
match_id = LobbyManager.available_rooms[selected_idx].get("match_id", "")
|
||||
|
||||
if AuthManager.is_guest:
|
||||
if LobbyManager.local_player_name.is_empty() or LobbyManager.local_player_name == "Player":
|
||||
LobbyManager.local_player_name = NameGenerator.generate_guest_name()
|
||||
else:
|
||||
LobbyManager.local_player_name = UserProfileManager.get_display_name()
|
||||
|
||||
if LobbyManager.is_lan_mode:
|
||||
if match_id.is_empty():
|
||||
var selected_items = [] if not lobby.room_list else lobby.room_list.get_selected_items()
|
||||
if selected_items.size() > 0:
|
||||
var idx = selected_items[0]
|
||||
if idx < LobbyManager.available_rooms.size():
|
||||
match_id = LobbyManager.available_rooms[idx].get("ip", "")
|
||||
|
||||
if match_id.is_empty():
|
||||
if lobby.connection_status: lobby.connection_status.text = "Enter Host IP to join"
|
||||
return
|
||||
|
||||
if lobby.connection_status: lobby.connection_status.text = "Connecting to %s..." % match_id
|
||||
lobby._apply_loadout_character()
|
||||
var ok = LobbyManager.join_room_lan(match_id)
|
||||
if not ok:
|
||||
if lobby.connection_status: lobby.connection_status.text = "Failed to connect to %s" % match_id
|
||||
else:
|
||||
if match_id.is_empty():
|
||||
if lobby.connection_status: lobby.connection_status.text = "No room selected"
|
||||
return
|
||||
if lobby.connection_status: lobby.connection_status.text = "Joining Nakama room..."
|
||||
lobby._apply_loadout_character()
|
||||
LobbyManager.join_room(match_id)
|
||||
|
||||
func _on_back_pressed() -> void:
|
||||
lobby._show_panel("main_menu")
|
||||
if lobby.connection_status: lobby.connection_status.text = ""
|
||||
|
||||
func on_room_list_updated(rooms: Array) -> void:
|
||||
if not lobby.room_list: return
|
||||
lobby.room_list.clear()
|
||||
|
||||
if lobby.item_template:
|
||||
var parent = lobby.item_template.get_parent()
|
||||
for child in parent.get_children():
|
||||
if child.has_meta("room_row"):
|
||||
child.queue_free()
|
||||
|
||||
var filtered := rooms.filter(func(r):
|
||||
return lobby._room_mode_filter.is_empty() or r.get("game_mode", "") == lobby._room_mode_filter
|
||||
)
|
||||
|
||||
if lobby.item_template:
|
||||
var parent = lobby.item_template.get_parent()
|
||||
for i in range(filtered.size()):
|
||||
var room = filtered[i]
|
||||
var row = lobby.item_template.duplicate()
|
||||
row.visible = true
|
||||
row.set_meta("room_row", true)
|
||||
row.set_meta("room_index", i)
|
||||
|
||||
var room_id_short = room.get("room_name", "???")
|
||||
var host_name = room.get("host_name", "Unknown")
|
||||
var player_count = room.get("player_count", 1)
|
||||
var max_players = room.get("max_players", 8)
|
||||
var mode = room.get("game_mode", "")
|
||||
var mode_short = "FMD" if mode == "Freemode" else ("SNG" if mode == "Stop n Go" else mode.left(3).to_upper())
|
||||
|
||||
var hbox = row.get_node_or_null("PanelContainer/MarginContainer/HBoxContainer")
|
||||
if hbox:
|
||||
var lbl_id = hbox.get_child(0)
|
||||
var lbl_host = hbox.get_child(1)
|
||||
var lbl_mode = hbox.get_child(2)
|
||||
var lbl_cap = hbox.get_child(3)
|
||||
if lbl_id: lbl_id.text = room_id_short
|
||||
if lbl_host: lbl_host.text = host_name
|
||||
if lbl_mode: lbl_mode.text = mode_short
|
||||
if lbl_cap: lbl_cap.text = "%d/%d" % [player_count, max_players]
|
||||
|
||||
parent.add_child(row)
|
||||
|
||||
var panel_box = row.get_node_or_null("PanelContainer")
|
||||
if panel_box:
|
||||
var idx = i
|
||||
panel_box.gui_input.connect(func(event: InputEvent):
|
||||
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
_on_room_selected(idx)
|
||||
if event.double_click:
|
||||
_on_room_activated(idx)
|
||||
)
|
||||
panel_box.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
else:
|
||||
for room in filtered:
|
||||
var room_name = room.get("room_name", "Unknown")
|
||||
var host_name = room.get("host_name", "Unknown")
|
||||
var player_count = room.get("player_count", 1)
|
||||
var max_players = room.get("max_players", 4)
|
||||
var mode = room.get("game_mode", "")
|
||||
var mode_tag = " [%s]" % mode if not mode.is_empty() else ""
|
||||
lobby.room_list.add_item("%s - %s (%d/%d)%s" % [room_name, host_name, player_count, max_players, mode_tag])
|
||||
|
||||
var filter_note = " [Filter: %s]" % lobby._room_mode_filter if not lobby._room_mode_filter.is_empty() else ""
|
||||
if filtered.size() == 0:
|
||||
if lobby.connection_status: lobby.connection_status.text = "No rooms available%s" % filter_note
|
||||
else:
|
||||
if lobby.connection_status: lobby.connection_status.text = "Found %d room(s)%s" % [filtered.size(), filter_note]
|
||||
@@ -0,0 +1 @@
|
||||
uid://drobqkt382tmp
|
||||
Reference in New Issue
Block a user