decdb74ade
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.
428 lines
16 KiB
GDScript
428 lines
16 KiB
GDScript
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 = ""
|