feat: Implement a new lobby system with configurable match duration, game over screen, and core game state management.
This commit is contained in:
+211
-178
@@ -14,22 +14,44 @@ extends Control
|
||||
@onready var join_btn = $RoomListPanel/VBoxContainer/ButtonContainer/JoinBtn
|
||||
@onready var back_btn = $RoomListPanel/VBoxContainer/ButtonContainer/BackBtn
|
||||
|
||||
# UI References - Lobby
|
||||
# UI References - Lobby Panel
|
||||
@onready var lobby_panel = $LobbyPanel
|
||||
@onready var room_name_header = $LobbyPanel/VBoxContainer/RoomNameHeader
|
||||
@onready var match_id_display = $LobbyPanel/VBoxContainer/MatchIdContainer/MatchIdDisplay
|
||||
@onready var copy_id_btn = $LobbyPanel/VBoxContainer/MatchIdContainer/CopyIdBtn
|
||||
@onready var player_list = $LobbyPanel/VBoxContainer/PlayerList
|
||||
@onready var status_label = $LobbyPanel/VBoxContainer/StatusLabel
|
||||
@onready var ready_btn = $LobbyPanel/VBoxContainer/ButtonContainer/ReadyBtn
|
||||
@onready var start_game_btn = $LobbyPanel/VBoxContainer/ButtonContainer/StartGameBtn
|
||||
@onready var leave_btn = $LobbyPanel/VBoxContainer/ButtonContainer/LeaveBtn
|
||||
@onready var room_name_header = $LobbyPanel/RoomNameHeader
|
||||
@onready var host_banner = $LobbyPanel/HostBanner
|
||||
@onready var host_banner_label = $LobbyPanel/HostBanner/HostBannerLabel
|
||||
|
||||
|
||||
# UI References - Top Bar
|
||||
@onready var profile_btn = $LobbyPanel/TopBar/ProfileSection/ProfileBtn
|
||||
@onready var logout_btn = $LobbyPanel/TopBar/ProfileSection/LogoutBtn
|
||||
@onready var match_id_display = $LobbyPanel/TopBar/MatchIdContainer/MatchIdDisplay
|
||||
@onready var copy_id_btn = $LobbyPanel/TopBar/MatchIdContainer/CopyIdBtn
|
||||
@onready var duration_option = $LobbyPanel/TopBar/SettingsSection/DurationOption
|
||||
@onready var duration_text_label = $LobbyPanel/TopBar/SettingsSection/DurationTextLabel
|
||||
|
||||
# UI References - Player Slots
|
||||
@onready var players_container = $LobbyPanel/PlayersContainer
|
||||
@onready var player_slots: Array[Control] = []
|
||||
|
||||
# UI References - Area Selector
|
||||
@onready var area_selector = $LobbyPanel/AreaSelector
|
||||
@onready var area_left_btn = $LobbyPanel/AreaSelector/AreaLeftBtn
|
||||
@onready var area_name_label = $LobbyPanel/AreaSelector/AreaName
|
||||
@onready var area_right_btn = $LobbyPanel/AreaSelector/AreaRightBtn
|
||||
|
||||
# UI References - Bottom Bar
|
||||
@onready var leave_btn = $LobbyPanel/BottomBar/LeaveBtn
|
||||
@onready var ready_btn = $LobbyPanel/BottomBar/ReadyBtn
|
||||
@onready var start_game_btn = $LobbyPanel/BottomBar/StartGameBtn
|
||||
@onready var status_label = $LobbyPanel/StatusLabel
|
||||
|
||||
# UI References - Status
|
||||
@onready var connection_status = $StatusBar/ConnectionStatus
|
||||
|
||||
# UI References - User Profile Bar (will be added to scene)
|
||||
var user_profile_bar: Control
|
||||
# Character preview textures
|
||||
var character_textures: Dictionary = {}
|
||||
|
||||
# Profile panel instance
|
||||
var profile_panel_instance: Control
|
||||
var admin_panel_instance: Control
|
||||
|
||||
@@ -39,27 +61,38 @@ var current_match_id: String = ""
|
||||
func _ready():
|
||||
# Check if user is authenticated
|
||||
if not AuthManager.is_logged_in():
|
||||
# Redirect to login screen - must use deferred call during _ready()
|
||||
call_deferred("_go_to_login")
|
||||
return
|
||||
|
||||
# Initialize user profile bar
|
||||
_setup_user_profile_bar()
|
||||
# Load character textures
|
||||
_load_character_textures()
|
||||
|
||||
# Get player slot references
|
||||
_setup_player_slots()
|
||||
|
||||
# Set player name from profile
|
||||
if player_name_input:
|
||||
player_name_input.text = UserProfileManager.get_display_name()
|
||||
|
||||
# Connect button signals
|
||||
# Connect button signals - Main Menu
|
||||
create_room_btn.pressed.connect(_on_create_room_pressed)
|
||||
browse_rooms_btn.pressed.connect(_on_browse_rooms_pressed)
|
||||
|
||||
# Connect button signals - Room List
|
||||
refresh_btn.pressed.connect(_on_refresh_pressed)
|
||||
join_btn.pressed.connect(_on_join_pressed)
|
||||
back_btn.pressed.connect(_on_back_pressed)
|
||||
|
||||
# Connect button signals - Lobby
|
||||
profile_btn.pressed.connect(_on_profile_btn_pressed)
|
||||
logout_btn.pressed.connect(_on_logout_pressed)
|
||||
copy_id_btn.pressed.connect(_on_copy_id_pressed)
|
||||
duration_option.item_selected.connect(_on_duration_selected)
|
||||
area_left_btn.pressed.connect(func(): LobbyManager.cycle_area(-1))
|
||||
area_right_btn.pressed.connect(func(): LobbyManager.cycle_area(1))
|
||||
leave_btn.pressed.connect(_on_leave_pressed)
|
||||
ready_btn.toggled.connect(_on_ready_toggled)
|
||||
start_game_btn.pressed.connect(_on_start_game_pressed)
|
||||
leave_btn.pressed.connect(_on_leave_pressed)
|
||||
copy_id_btn.pressed.connect(_on_copy_id_pressed)
|
||||
|
||||
# Connect LobbyManager signals
|
||||
LobbyManager.room_list_updated.connect(_on_room_list_updated)
|
||||
@@ -70,6 +103,10 @@ func _ready():
|
||||
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.character_changed.connect(_on_character_changed)
|
||||
LobbyManager.area_changed.connect(_on_area_changed)
|
||||
LobbyManager.player_list_changed.connect(_update_player_slots)
|
||||
|
||||
# Connect NakamaManager signals
|
||||
NakamaManager.connected_to_nakama.connect(_on_connected_to_nakama)
|
||||
@@ -77,144 +114,35 @@ func _ready():
|
||||
|
||||
# Show main menu initially
|
||||
_show_panel("main_menu")
|
||||
_update_profile_bar()
|
||||
|
||||
# =============================================================================
|
||||
# User Profile Bar
|
||||
# Setup
|
||||
# =============================================================================
|
||||
|
||||
func _setup_user_profile_bar() -> void:
|
||||
# Create profile bar dynamically (or get reference if in scene)
|
||||
user_profile_bar = _create_profile_bar()
|
||||
add_child(user_profile_bar)
|
||||
func _load_character_textures() -> void:
|
||||
"""Load character expression textures for preview."""
|
||||
var characters = ["Bob", "Gatot", "Masbro", "Oldpop"]
|
||||
for char_name in characters:
|
||||
var tex_path = "res://assets/characters/%s_%s-expression.png" % [char_name, char_name.to_lower()]
|
||||
if ResourceLoader.exists(tex_path):
|
||||
character_textures[char_name] = load(tex_path)
|
||||
else:
|
||||
print("[Lobby] Character texture not found: ", tex_path)
|
||||
|
||||
func _create_profile_bar() -> Control:
|
||||
var bar := HBoxContainer.new()
|
||||
bar.name = "UserProfileBar"
|
||||
bar.set_anchors_preset(Control.PRESET_TOP_WIDE)
|
||||
bar.offset_top = 5
|
||||
bar.offset_bottom = 45
|
||||
bar.offset_left = 10
|
||||
bar.offset_right = -10
|
||||
|
||||
# Avatar
|
||||
var avatar := TextureRect.new()
|
||||
avatar.name = "Avatar"
|
||||
avatar.custom_minimum_size = Vector2(35, 35)
|
||||
avatar.expand_mode = TextureRect.EXPAND_FIT_WIDTH
|
||||
avatar.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
bar.add_child(avatar)
|
||||
|
||||
# Display name
|
||||
var name_label := Label.new()
|
||||
name_label.name = "DisplayName"
|
||||
name_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
name_label.add_theme_color_override("font_color", Color(0, 0.831, 1))
|
||||
bar.add_child(name_label)
|
||||
|
||||
# Account type badge
|
||||
var badge := Label.new()
|
||||
badge.name = "AccountBadge"
|
||||
badge.add_theme_font_size_override("font_size", 10)
|
||||
badge.add_theme_color_override("font_color", Color(0.5, 0.5, 0.6))
|
||||
bar.add_child(badge)
|
||||
|
||||
# Profile button
|
||||
var profile_btn := Button.new()
|
||||
profile_btn.name = "ProfileBtn"
|
||||
profile_btn.text = "Profile"
|
||||
profile_btn.pressed.connect(_on_profile_btn_pressed)
|
||||
bar.add_child(profile_btn)
|
||||
|
||||
# Admin button (only visible for admins/hosts)
|
||||
var admin_btn := Button.new()
|
||||
admin_btn.name = "AdminBtn"
|
||||
admin_btn.text = "Admin"
|
||||
admin_btn.visible = false # Hidden by default, shown if user is admin
|
||||
admin_btn.pressed.connect(_on_admin_btn_pressed)
|
||||
bar.add_child(admin_btn)
|
||||
|
||||
# Logout button
|
||||
var logout_btn := Button.new()
|
||||
logout_btn.name = "LogoutBtn"
|
||||
logout_btn.text = "Logout"
|
||||
logout_btn.pressed.connect(_on_logout_pressed)
|
||||
bar.add_child(logout_btn)
|
||||
|
||||
return bar
|
||||
|
||||
func _update_profile_bar() -> void:
|
||||
if not user_profile_bar:
|
||||
return
|
||||
|
||||
var name_label := user_profile_bar.get_node_or_null("DisplayName") as Label
|
||||
if name_label:
|
||||
name_label.text = UserProfileManager.get_display_name()
|
||||
|
||||
var badge := user_profile_bar.get_node_or_null("AccountBadge") as Label
|
||||
if badge:
|
||||
badge.text = "[Guest]" if AuthManager.is_guest else "[Registered]"
|
||||
|
||||
var avatar := user_profile_bar.get_node_or_null("Avatar") as TextureRect
|
||||
if avatar:
|
||||
var avatar_url := UserProfileManager.get_avatar_url()
|
||||
if ResourceLoader.exists(avatar_url):
|
||||
avatar.texture = load(avatar_url)
|
||||
|
||||
# Show admin button if user is admin or host
|
||||
var admin_btn := user_profile_bar.get_node_or_null("AdminBtn") as Button
|
||||
if admin_btn:
|
||||
# Check if user is admin (you can define admin check logic here)
|
||||
var is_admin = _check_if_admin()
|
||||
admin_btn.visible = is_admin
|
||||
|
||||
func _on_profile_btn_pressed() -> void:
|
||||
# Show profile panel
|
||||
if not profile_panel_instance:
|
||||
var profile_panel_scene := load("res://scenes/ui/profile_panel.tscn")
|
||||
profile_panel_instance = profile_panel_scene.instantiate()
|
||||
profile_panel_instance.closed.connect(func(): profile_panel_instance.hide())
|
||||
profile_panel_instance.profile_updated.connect(_update_profile_bar)
|
||||
add_child(profile_panel_instance)
|
||||
|
||||
profile_panel_instance.show_panel()
|
||||
# Center the panel
|
||||
profile_panel_instance.position = (get_viewport_rect().size - profile_panel_instance.size) / 2
|
||||
|
||||
func _on_logout_pressed() -> void:
|
||||
AuthManager.logout()
|
||||
_go_to_login()
|
||||
|
||||
func _go_to_login() -> void:
|
||||
if get_tree():
|
||||
get_tree().change_scene_to_file("res://scenes/ui/login_screen.tscn")
|
||||
|
||||
func _on_admin_btn_pressed() -> void:
|
||||
# Show admin panel
|
||||
if not admin_panel_instance:
|
||||
var admin_panel_scene := load("res://scenes/ui/admin_panel.tscn")
|
||||
admin_panel_instance = admin_panel_scene.instantiate()
|
||||
# Connect close signal if available
|
||||
if admin_panel_instance.has_signal("closed"):
|
||||
admin_panel_instance.closed.connect(func(): admin_panel_instance.hide())
|
||||
add_child(admin_panel_instance)
|
||||
|
||||
admin_panel_instance.show()
|
||||
# Center the panel
|
||||
admin_panel_instance.position = (get_viewport_rect().size - admin_panel_instance.size) / 2
|
||||
|
||||
func _check_if_admin() -> bool:
|
||||
# Check if user is admin - can be host or have specific admin role
|
||||
# You can extend this to check Nakama user metadata or roles
|
||||
if LobbyManager.is_host:
|
||||
return true
|
||||
|
||||
# Check if user has admin role in their profile (optional)
|
||||
var user_id = AuthManager.get_user_id() if AuthManager.has_method("get_user_id") else ""
|
||||
# Add your admin user IDs here or check from Nakama metadata
|
||||
var admin_user_ids = ["admin_user_id_1", "admin_user_id_2"] # Configure as needed
|
||||
|
||||
return user_id in admin_user_ids
|
||||
func _setup_player_slots() -> void:
|
||||
"""Get references to all player slot nodes."""
|
||||
player_slots.clear()
|
||||
for i in range(1, 5):
|
||||
var slot = players_container.get_node_or_null("PlayerSlot%d" % i)
|
||||
if slot:
|
||||
player_slots.append(slot)
|
||||
# Connect character navigation buttons for all slots
|
||||
var left_btn = slot.get_node_or_null("CharacterNav%d/CharLeftBtn%d" % [i, i])
|
||||
var right_btn = slot.get_node_or_null("CharacterNav%d/CharRightBtn%d" % [i, i])
|
||||
if left_btn:
|
||||
left_btn.pressed.connect(func(): LobbyManager.cycle_character(-1))
|
||||
if right_btn:
|
||||
right_btn.pressed.connect(func(): LobbyManager.cycle_character(1))
|
||||
|
||||
# =============================================================================
|
||||
# Panel Management
|
||||
@@ -230,13 +158,11 @@ func _show_panel(panel_name: String) -> void:
|
||||
# =============================================================================
|
||||
|
||||
func _on_create_room_pressed() -> void:
|
||||
# Set player name
|
||||
LobbyManager.local_player_name = player_name_input.text.strip_edges()
|
||||
if LobbyManager.local_player_name.is_empty():
|
||||
LobbyManager.local_player_name = "Host"
|
||||
|
||||
connection_status.text = "Creating room..."
|
||||
# Room name auto-generated since Nakama doesn't support custom names
|
||||
LobbyManager.create_room("Room %d" % randi_range(1000, 9999))
|
||||
|
||||
func _on_browse_rooms_pressed() -> void:
|
||||
@@ -254,11 +180,9 @@ func _on_refresh_pressed() -> void:
|
||||
LobbyManager.refresh_room_list()
|
||||
|
||||
func _on_join_pressed() -> void:
|
||||
# First check if there's a match ID entered
|
||||
var match_id = match_id_input.text.strip_edges()
|
||||
|
||||
if match_id.is_empty():
|
||||
# Try to use selected room from list
|
||||
var selected_items = room_list.get_selected_items()
|
||||
if selected_items.size() == 0:
|
||||
connection_status.text = "Please select a room or enter Match ID"
|
||||
@@ -272,7 +196,6 @@ func _on_join_pressed() -> void:
|
||||
connection_status.text = "No room selected"
|
||||
return
|
||||
|
||||
# Set player name
|
||||
LobbyManager.local_player_name = player_name_input.text.strip_edges()
|
||||
if LobbyManager.local_player_name.is_empty():
|
||||
LobbyManager.local_player_name = "Player"
|
||||
@@ -303,7 +226,33 @@ func _on_leave_pressed() -> void:
|
||||
|
||||
func _on_copy_id_pressed() -> void:
|
||||
DisplayServer.clipboard_set(current_match_id)
|
||||
connection_status.text = "Match ID copied to clipboard!"
|
||||
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_profile_btn_pressed() -> void:
|
||||
if not profile_panel_instance:
|
||||
var profile_panel_scene := load("res://scenes/ui/profile_panel.tscn")
|
||||
profile_panel_instance = profile_panel_scene.instantiate()
|
||||
profile_panel_instance.closed.connect(func(): profile_panel_instance.hide())
|
||||
add_child(profile_panel_instance)
|
||||
|
||||
profile_panel_instance.show_panel()
|
||||
profile_panel_instance.position = (get_viewport_rect().size - profile_panel_instance.size) / 2
|
||||
|
||||
func _on_logout_pressed() -> void:
|
||||
AuthManager.logout()
|
||||
_go_to_login()
|
||||
|
||||
func _go_to_login() -> void:
|
||||
if get_tree():
|
||||
get_tree().change_scene_to_file("res://scenes/ui/login_screen.tscn")
|
||||
|
||||
# =============================================================================
|
||||
# LobbyManager Signal Handlers
|
||||
@@ -327,12 +276,25 @@ func _on_room_joined(room_data: Dictionary) -> void:
|
||||
_show_panel("lobby")
|
||||
current_match_id = room_data.get("match_id", "")
|
||||
room_name_header.text = "ROOM: %s" % room_data.get("room_name", "Unknown")
|
||||
match_id_display.text = "Match ID: %s " % current_match_id
|
||||
match_id_display.text = "ID: %s" % _truncate_id(current_match_id)
|
||||
|
||||
# Update start button visibility (host only)
|
||||
start_game_btn.visible = LobbyManager.is_host
|
||||
# Configure host-specific UI
|
||||
var is_host = LobbyManager.is_host
|
||||
host_banner.visible = is_host
|
||||
start_game_btn.visible = is_host
|
||||
|
||||
_update_player_list()
|
||||
# Duration: host sees dropdown, clients see text
|
||||
duration_option.visible = is_host
|
||||
duration_text_label.visible = not is_host
|
||||
if not is_host:
|
||||
_update_duration_text_label(LobbyManager.get_match_duration())
|
||||
|
||||
# Area selector: only host can interact
|
||||
area_left_btn.disabled = not is_host
|
||||
area_right_btn.disabled = not is_host
|
||||
area_name_label.text = LobbyManager.get_selected_area()
|
||||
|
||||
_update_player_slots()
|
||||
connection_status.text = "Connected to room"
|
||||
|
||||
func _on_room_left() -> void:
|
||||
@@ -340,33 +302,38 @@ func _on_room_left() -> void:
|
||||
connection_status.text = "Left room"
|
||||
|
||||
func _on_player_joined(player_data: Dictionary) -> void:
|
||||
_update_player_list()
|
||||
_update_player_slots()
|
||||
status_label.text = "%s joined!" % player_data.get("name", "Player")
|
||||
|
||||
func _on_player_left(_player_id: int) -> void:
|
||||
_update_player_list()
|
||||
_update_player_slots()
|
||||
status_label.text = "A player left"
|
||||
|
||||
func _on_ready_state_changed(_player_id: int, _is_ready: bool) -> void:
|
||||
_update_player_list()
|
||||
_update_player_slots()
|
||||
_update_status()
|
||||
|
||||
func _on_all_players_ready() -> void:
|
||||
if LobbyManager.is_host:
|
||||
start_game_btn.disabled = false
|
||||
status_label.text = "All players ready! Host can start."
|
||||
status_label.text = "All ready! Start the match!"
|
||||
else:
|
||||
status_label.text = "All players ready! Waiting for host..."
|
||||
status_label.text = "All ready! Waiting for host..."
|
||||
|
||||
func _on_game_starting() -> void:
|
||||
connection_status.text = "Starting game..."
|
||||
# Small delay for visual feedback
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
get_tree().change_scene_to_file("res://scenes/main.tscn")
|
||||
|
||||
# =============================================================================
|
||||
# NakamaManager Signal Handlers
|
||||
# =============================================================================
|
||||
func _on_match_duration_changed(duration_seconds: int) -> void:
|
||||
if not LobbyManager.is_host:
|
||||
_update_duration_text_label(duration_seconds)
|
||||
|
||||
func _on_character_changed(_player_id: int, _character_name: String) -> void:
|
||||
_update_player_slots()
|
||||
|
||||
func _on_area_changed(area_name: String) -> void:
|
||||
area_name_label.text = area_name
|
||||
|
||||
func _on_connected_to_nakama() -> void:
|
||||
connection_status.text = "Connected to server"
|
||||
@@ -376,18 +343,64 @@ func _on_connection_failed(error_message: String) -> void:
|
||||
_show_panel("main_menu")
|
||||
|
||||
# =============================================================================
|
||||
# Helper Functions
|
||||
# Player Slot Updates
|
||||
# =============================================================================
|
||||
|
||||
func _update_player_list() -> void:
|
||||
player_list.clear()
|
||||
func _update_player_slots() -> void:
|
||||
"""Update all player slot visuals based on current player list."""
|
||||
var players = LobbyManager.get_players()
|
||||
for player in players:
|
||||
var player_name = player.get("name", "Unknown")
|
||||
var is_ready = player.get("is_ready", false)
|
||||
var ready_icon = " ✓" if is_ready else " ✗"
|
||||
var host_tag = " (Host)" if player.get("id") == 1 else ""
|
||||
player_list.add_item("%s%s%s" % [player_name, host_tag, ready_icon])
|
||||
var my_id = multiplayer.get_unique_id()
|
||||
|
||||
for i in range(player_slots.size()):
|
||||
var slot = player_slots[i]
|
||||
var slot_num = i + 1
|
||||
|
||||
if i < players.size():
|
||||
var player = players[i]
|
||||
slot.visible = true
|
||||
|
||||
# Update player name
|
||||
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
|
||||
|
||||
# Update character preview
|
||||
var char_preview = slot.get_node_or_null("CharacterPreview%d" % slot_num)
|
||||
var char_name = player.get("character", "Bob")
|
||||
if char_preview and character_textures.has(char_name):
|
||||
char_preview.texture = character_textures[char_name]
|
||||
|
||||
# Check if this is the local player
|
||||
var is_local_player = player.get("id") == my_id
|
||||
|
||||
# Update character name in nav (inside CharacterNav)
|
||||
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
|
||||
|
||||
# Update character name label (outside nav, for non-local players)
|
||||
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
|
||||
|
||||
# Show/hide character navigation (only for local player)
|
||||
var char_nav = slot.get_node_or_null("CharacterNav%d" % slot_num)
|
||||
if char_nav:
|
||||
char_nav.visible = is_local_player
|
||||
|
||||
# Update ready status
|
||||
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))
|
||||
else:
|
||||
slot.visible = false
|
||||
|
||||
func _update_status() -> void:
|
||||
var players = LobbyManager.get_players()
|
||||
@@ -400,3 +413,23 @@ func _update_status() -> void:
|
||||
|
||||
if LobbyManager.is_host:
|
||||
start_game_btn.disabled = not LobbyManager.is_all_ready()
|
||||
|
||||
# =============================================================================
|
||||
# Helper Functions
|
||||
# =============================================================================
|
||||
|
||||
func _update_duration_text_label(duration_seconds: int) -> void:
|
||||
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
|
||||
duration_text_label.text = duration_text
|
||||
|
||||
func _truncate_id(id: String) -> String:
|
||||
if id.length() > 16:
|
||||
return id.substr(0, 8) + "..." + id.substr(-4)
|
||||
return id
|
||||
|
||||
+479
-59
@@ -20,7 +20,7 @@ anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.12, 0.1, 0.08, 1)
|
||||
color = Color(0.08, 0.12, 0.18, 1)
|
||||
|
||||
[node name="MainMenuPanel" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
@@ -165,90 +165,507 @@ custom_minimum_size = Vector2(110, 44)
|
||||
layout_mode = 2
|
||||
text = "BACK"
|
||||
|
||||
[node name="LobbyPanel" type="PanelContainer" parent="."]
|
||||
[node name="LobbyPanel" type="Control" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="LobbyBackground" type="ColorRect" parent="LobbyPanel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.06, 0.08, 0.12, 1)
|
||||
|
||||
[node name="TopBar" type="HBoxContainer" parent="LobbyPanel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_left = 20.0
|
||||
offset_top = 10.0
|
||||
offset_right = -20.0
|
||||
offset_bottom = 50.0
|
||||
grow_horizontal = 2
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="ProfileSection" type="HBoxContainer" parent="LobbyPanel/TopBar"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="ProfileBtn" type="Button" parent="LobbyPanel/TopBar/ProfileSection"]
|
||||
custom_minimum_size = Vector2(80, 32)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 11
|
||||
text = "Profile"
|
||||
|
||||
[node name="LogoutBtn" type="Button" parent="LobbyPanel/TopBar/ProfileSection"]
|
||||
custom_minimum_size = Vector2(70, 32)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 11
|
||||
text = "Logout"
|
||||
|
||||
[node name="Spacer" type="Control" parent="LobbyPanel/TopBar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="MatchIdContainer" type="HBoxContainer" parent="LobbyPanel/TopBar"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="MatchIdDisplay" type="Label" parent="LobbyPanel/TopBar/MatchIdContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.5, 0.5, 0.55, 1)
|
||||
theme_override_font_sizes/font_size = 10
|
||||
text = "Match ID: "
|
||||
|
||||
[node name="CopyIdBtn" type="Button" parent="LobbyPanel/TopBar/MatchIdContainer"]
|
||||
custom_minimum_size = Vector2(60, 28)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 10
|
||||
text = "COPY"
|
||||
|
||||
[node name="Spacer2" type="Control" parent="LobbyPanel/TopBar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="SettingsSection" type="HBoxContainer" parent="LobbyPanel/TopBar"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="DurationLabel" type="Label" parent="LobbyPanel/TopBar/SettingsSection"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1)
|
||||
theme_override_font_sizes/font_size = 11
|
||||
text = "Duration:"
|
||||
|
||||
[node name="DurationOption" type="OptionButton" parent="LobbyPanel/TopBar/SettingsSection"]
|
||||
custom_minimum_size = Vector2(90, 28)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 11
|
||||
selected = 2
|
||||
item_count = 5
|
||||
popup/item_0/text = "1 min"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "2 min"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "3 min"
|
||||
popup/item_2/id = 2
|
||||
popup/item_3/text = "5 min"
|
||||
popup/item_3/id = 3
|
||||
popup/item_4/text = "10 min"
|
||||
popup/item_4/id = 4
|
||||
|
||||
[node name="DurationTextLabel" type="Label" parent="LobbyPanel/TopBar/SettingsSection"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.647, 0.996, 0.224, 1)
|
||||
theme_override_font_sizes/font_size = 11
|
||||
text = "3 min"
|
||||
|
||||
[node name="HostBanner" type="PanelContainer" parent="LobbyPanel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 5
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
offset_left = -150.0
|
||||
offset_top = 99.0
|
||||
offset_right = 150.0
|
||||
offset_bottom = 159.0
|
||||
grow_horizontal = 2
|
||||
|
||||
[node name="HostBannerLabel" type="Label" parent="LobbyPanel/HostBanner"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.1, 0.1, 0.1, 1)
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "HOST"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="RoomNameHeader" type="Label" parent="LobbyPanel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 5
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
offset_left = -200.0
|
||||
offset_top = 55.0
|
||||
offset_right = 200.0
|
||||
offset_bottom = 95.0
|
||||
grow_horizontal = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 20
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="PlayersContainer" type="HBoxContainer" parent="LobbyPanel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -300.0
|
||||
offset_top = -260.0
|
||||
offset_right = 300.0
|
||||
offset_bottom = 260.0
|
||||
offset_left = -450.0
|
||||
offset_top = -120.0
|
||||
offset_right = 450.0
|
||||
offset_bottom = 200.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 30
|
||||
alignment = 1
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="LobbyPanel"]
|
||||
[node name="PlayerSlot1" type="VBoxContainer" parent="LobbyPanel/PlayersContainer"]
|
||||
custom_minimum_size = Vector2(180, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 14
|
||||
theme_override_constants/separation = 8
|
||||
alignment = 1
|
||||
|
||||
[node name="RoomNameHeader" type="Label" parent="LobbyPanel/VBoxContainer"]
|
||||
[node name="PlayerName1" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot1"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.647, 0.996, 0.224, 1)
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "ROOM: "
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "Player 1"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="MatchIdContainer" type="HBoxContainer" parent="LobbyPanel/VBoxContainer"]
|
||||
[node name="CharacterPreview1" type="TextureRect" parent="LobbyPanel/PlayersContainer/PlayerSlot1"]
|
||||
custom_minimum_size = Vector2(140, 140)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="CharacterNav1" type="HBoxContainer" parent="LobbyPanel/PlayersContainer/PlayerSlot1"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="MatchIdDisplay" type="Label" parent="LobbyPanel/VBoxContainer/MatchIdContainer"]
|
||||
[node name="CharLeftBtn1" type="Button" parent="LobbyPanel/PlayersContainer/PlayerSlot1/CharacterNav1"]
|
||||
custom_minimum_size = Vector2(40, 36)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.5, 0.5, 0.55, 1)
|
||||
theme_override_font_sizes/font_size = 10
|
||||
text = "Match ID: "
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "◀"
|
||||
|
||||
[node name="CopyIdBtn" type="Button" parent="LobbyPanel/VBoxContainer/MatchIdContainer"]
|
||||
custom_minimum_size = Vector2(80, 32)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 10
|
||||
text = "COPY"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="LobbyPanel/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="PlayersLabel" type="Label" parent="LobbyPanel/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1)
|
||||
theme_override_font_sizes/font_size = 13
|
||||
text = "PLAYERS"
|
||||
|
||||
[node name="PlayerList" type="ItemList" parent="LobbyPanel/VBoxContainer"]
|
||||
custom_minimum_size = Vector2(0, 160)
|
||||
layout_mode = 2
|
||||
allow_reselect = true
|
||||
|
||||
[node name="StatusLabel" type="Label" parent="LobbyPanel/VBoxContainer"]
|
||||
[node name="CharacterName1" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot1/CharacterNav1"]
|
||||
custom_minimum_size = Vector2(80, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "Waiting for players..."
|
||||
text = "Bob"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="CharRightBtn1" type="Button" parent="LobbyPanel/PlayersContainer/PlayerSlot1/CharacterNav1"]
|
||||
custom_minimum_size = Vector2(40, 36)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "▶"
|
||||
|
||||
[node name="CharacterNameLabel1" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot1"]
|
||||
custom_minimum_size = Vector2(80, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "Bob"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="ButtonContainer" type="HBoxContainer" parent="LobbyPanel/VBoxContainer"]
|
||||
[node name="ReadyStatus1" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot1"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 14
|
||||
theme_override_colors/font_color = Color(0.6, 0.6, 0.6, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "NOT READY"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="PlayerSlot2" type="VBoxContainer" parent="LobbyPanel/PlayersContainer"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(180, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
alignment = 1
|
||||
|
||||
[node name="ReadyBtn" type="Button" parent="LobbyPanel/VBoxContainer/ButtonContainer"]
|
||||
custom_minimum_size = Vector2(110, 48)
|
||||
[node name="PlayerName2" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot2"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "Player 2"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="CharacterPreview2" type="TextureRect" parent="LobbyPanel/PlayersContainer/PlayerSlot2"]
|
||||
custom_minimum_size = Vector2(140, 140)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="CharacterNav2" type="HBoxContainer" parent="LobbyPanel/PlayersContainer/PlayerSlot2"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="CharLeftBtn2" type="Button" parent="LobbyPanel/PlayersContainer/PlayerSlot2/CharacterNav2"]
|
||||
custom_minimum_size = Vector2(40, 36)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "◀"
|
||||
|
||||
[node name="CharacterName2" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot2/CharacterNav2"]
|
||||
custom_minimum_size = Vector2(80, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "Bob"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="CharRightBtn2" type="Button" parent="LobbyPanel/PlayersContainer/PlayerSlot2/CharacterNav2"]
|
||||
custom_minimum_size = Vector2(40, 36)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "▶"
|
||||
|
||||
[node name="CharacterNameLabel2" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot2"]
|
||||
custom_minimum_size = Vector2(80, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "Bob"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="ReadyStatus2" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot2"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.6, 0.6, 0.6, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "NOT READY"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="PlayerSlot3" type="VBoxContainer" parent="LobbyPanel/PlayersContainer"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(180, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
alignment = 1
|
||||
|
||||
[node name="PlayerName3" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot3"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "Player 3"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="CharacterPreview3" type="TextureRect" parent="LobbyPanel/PlayersContainer/PlayerSlot3"]
|
||||
custom_minimum_size = Vector2(140, 140)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="CharacterNav3" type="HBoxContainer" parent="LobbyPanel/PlayersContainer/PlayerSlot3"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="CharLeftBtn3" type="Button" parent="LobbyPanel/PlayersContainer/PlayerSlot3/CharacterNav3"]
|
||||
custom_minimum_size = Vector2(40, 36)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "◀"
|
||||
|
||||
[node name="CharacterName3" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot3/CharacterNav3"]
|
||||
custom_minimum_size = Vector2(80, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "Bob"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="CharRightBtn3" type="Button" parent="LobbyPanel/PlayersContainer/PlayerSlot3/CharacterNav3"]
|
||||
custom_minimum_size = Vector2(40, 36)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "▶"
|
||||
|
||||
[node name="CharacterNameLabel3" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot3"]
|
||||
custom_minimum_size = Vector2(80, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "Bob"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="ReadyStatus3" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot3"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.6, 0.6, 0.6, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "NOT READY"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="PlayerSlot4" type="VBoxContainer" parent="LobbyPanel/PlayersContainer"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(180, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
alignment = 1
|
||||
|
||||
[node name="PlayerName4" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot4"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "Player 4"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="CharacterPreview4" type="TextureRect" parent="LobbyPanel/PlayersContainer/PlayerSlot4"]
|
||||
custom_minimum_size = Vector2(140, 140)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="CharacterNav4" type="HBoxContainer" parent="LobbyPanel/PlayersContainer/PlayerSlot4"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="CharLeftBtn4" type="Button" parent="LobbyPanel/PlayersContainer/PlayerSlot4/CharacterNav4"]
|
||||
custom_minimum_size = Vector2(40, 36)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "◀"
|
||||
|
||||
[node name="CharacterName4" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot4/CharacterNav4"]
|
||||
custom_minimum_size = Vector2(80, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "Bob"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="CharRightBtn4" type="Button" parent="LobbyPanel/PlayersContainer/PlayerSlot4/CharacterNav4"]
|
||||
custom_minimum_size = Vector2(40, 36)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "▶"
|
||||
|
||||
[node name="CharacterNameLabel4" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot4"]
|
||||
custom_minimum_size = Vector2(80, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "Bob"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="ReadyStatus4" type="Label" parent="LobbyPanel/PlayersContainer/PlayerSlot4"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.6, 0.6, 0.6, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "NOT READY"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="AreaSelector" type="HBoxContainer" parent="LobbyPanel"]
|
||||
clip_contents = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 7
|
||||
anchor_left = 0.5
|
||||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -660.0
|
||||
offset_top = -137.0
|
||||
offset_right = -412.0
|
||||
offset_bottom = -87.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
|
||||
[node name="AreaLabel" type="Label" parent="LobbyPanel/AreaSelector"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1)
|
||||
theme_override_font_sizes/font_size = 13
|
||||
text = "AREA: "
|
||||
|
||||
[node name="AreaLeftBtn" type="Button" parent="LobbyPanel/AreaSelector"]
|
||||
custom_minimum_size = Vector2(40, 36)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "◀"
|
||||
|
||||
[node name="AreaName" type="Label" parent="LobbyPanel/AreaSelector"]
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.647, 0.996, 0.224, 1)
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "Desert"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="AreaRightBtn" type="Button" parent="LobbyPanel/AreaSelector"]
|
||||
custom_minimum_size = Vector2(40, 36)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "▶"
|
||||
|
||||
[node name="BottomBar" type="HBoxContainer" parent="LobbyPanel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 20.0
|
||||
offset_top = -70.0
|
||||
offset_right = -20.0
|
||||
offset_bottom = -20.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="LeaveBtn" type="Button" parent="LobbyPanel/BottomBar"]
|
||||
custom_minimum_size = Vector2(120, 44)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "LEAVE"
|
||||
|
||||
[node name="Spacer" type="Control" parent="LobbyPanel/BottomBar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ReadyBtn" type="Button" parent="LobbyPanel/BottomBar"]
|
||||
custom_minimum_size = Vector2(140, 50)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 16
|
||||
toggle_mode = true
|
||||
text = "READY"
|
||||
|
||||
[node name="StartGameBtn" type="Button" parent="LobbyPanel/VBoxContainer/ButtonContainer"]
|
||||
custom_minimum_size = Vector2(140, 48)
|
||||
[node name="StartGameBtn" type="Button" parent="LobbyPanel/BottomBar"]
|
||||
custom_minimum_size = Vector2(140, 50)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 16
|
||||
disabled = true
|
||||
text = "START GAME"
|
||||
text = "MATCH"
|
||||
|
||||
[node name="LeaveBtn" type="Button" parent="LobbyPanel/VBoxContainer/ButtonContainer"]
|
||||
custom_minimum_size = Vector2(110, 48)
|
||||
layout_mode = 2
|
||||
text = "LEAVE"
|
||||
[node name="StatusLabel" type="Label" parent="LobbyPanel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 7
|
||||
anchor_left = 0.5
|
||||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -150.0
|
||||
offset_top = -90.0
|
||||
offset_right = 150.0
|
||||
offset_bottom = -75.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="PlayerList" type="ItemList" parent="LobbyPanel"]
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
|
||||
[node name="StatusBar" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
@@ -256,10 +673,10 @@ anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 24.0
|
||||
offset_top = -72.0
|
||||
offset_right = -24.0
|
||||
offset_bottom = -24.0
|
||||
offset_left = 464.0
|
||||
offset_top = -70.0
|
||||
offset_right = -461.0
|
||||
offset_bottom = -21.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
|
||||
@@ -272,14 +689,17 @@ horizontal_alignment = 1
|
||||
|
||||
[node name="VersionLabel" type="Label" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -130.0
|
||||
offset_top = 20.0
|
||||
offset_right = -20.0
|
||||
offset_bottom = 40.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -124.0
|
||||
offset_top = -95.0
|
||||
offset_right = -14.0
|
||||
offset_bottom = -75.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
theme_override_colors/font_color = Color(0.6, 0.6, 0.6, 0.6)
|
||||
theme_override_font_sizes/font_size = 11
|
||||
text = "v0.1.0 ALPHA"
|
||||
|
||||
+203
-2
@@ -30,6 +30,7 @@ func _ready():
|
||||
ui_manager.setup_leaderboard_ui(self)
|
||||
ui_manager.setup_powerup_bar_ui(self)
|
||||
_setup_obstacle_ui()
|
||||
_setup_global_match_timer_ui()
|
||||
|
||||
# Auto-start game if coming from lobby (already connected to match)
|
||||
if NakamaManager.is_connected_to_nakama() and multiplayer.get_unique_id() != 0:
|
||||
@@ -59,6 +60,8 @@ func _init_managers():
|
||||
goals_cycle_manager.timer_updated.connect(_on_timer_updated)
|
||||
goals_cycle_manager.score_updated.connect(_on_score_updated)
|
||||
goals_cycle_manager.leaderboard_updated.connect(_on_leaderboard_updated)
|
||||
goals_cycle_manager.global_timer_updated.connect(_on_global_timer_updated)
|
||||
goals_cycle_manager.match_ended.connect(_on_match_ended)
|
||||
|
||||
# Message Bar Configuration
|
||||
const MAX_MESSAGES := 5
|
||||
@@ -181,6 +184,54 @@ func _setup_obstacle_ui():
|
||||
)
|
||||
$ActionMenu/ActionButtonContainer.add_child(type_button)
|
||||
|
||||
func _setup_global_match_timer_ui():
|
||||
"""Create the global match timer display at the top of the screen."""
|
||||
var existing = get_node_or_null("GlobalMatchTimer")
|
||||
if existing:
|
||||
return
|
||||
|
||||
# Create timer panel
|
||||
var panel = PanelContainer.new()
|
||||
panel.name = "GlobalMatchTimer"
|
||||
|
||||
# Position at top center
|
||||
panel.set_anchors_preset(Control.PRESET_CENTER_TOP)
|
||||
panel.offset_left = -80
|
||||
panel.offset_right = 80
|
||||
panel.offset_top = 10
|
||||
panel.offset_bottom = 60
|
||||
|
||||
# Style
|
||||
var style = StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.1, 0.1, 0.15, 0.9)
|
||||
style.border_width_left = 2
|
||||
style.border_width_top = 2
|
||||
style.border_width_right = 2
|
||||
style.border_width_bottom = 2
|
||||
style.border_color = Color(0.647, 0.996, 0.224, 0.8)
|
||||
style.corner_radius_top_left = 8
|
||||
style.corner_radius_top_right = 8
|
||||
style.corner_radius_bottom_right = 8
|
||||
style.corner_radius_bottom_left = 8
|
||||
panel.add_theme_stylebox_override("panel", style)
|
||||
|
||||
# VBox for content
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.name = "VBox"
|
||||
vbox.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
panel.add_child(vbox)
|
||||
|
||||
# Label
|
||||
var label = Label.new()
|
||||
label.name = "TimerLabel"
|
||||
label.text = "3:00"
|
||||
label.add_theme_font_size_override("font_size", 28)
|
||||
label.add_theme_color_override("font_color", Color(0.647, 0.996, 0.224))
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
vbox.add_child(label)
|
||||
|
||||
add_child(panel)
|
||||
|
||||
func _process(delta):
|
||||
if multiplayer.is_server() and GameStateManager.is_game_started():
|
||||
if TurnManager.turn_based_mode:
|
||||
@@ -322,9 +373,10 @@ func _start_game():
|
||||
var next_player = TurnManager.next_turn(GameStateManager.players)
|
||||
rpc("set_current_turn", next_player)
|
||||
|
||||
# Start the goals cycle timer
|
||||
# Start the global match timer (this also starts the first cycle)
|
||||
if goals_cycle_manager:
|
||||
goals_cycle_manager.start_cycle()
|
||||
var match_duration = LobbyManager.get_match_duration()
|
||||
goals_cycle_manager.start_match(float(match_duration))
|
||||
|
||||
# Initialize leaderboard with all players
|
||||
if ui_manager:
|
||||
@@ -830,6 +882,155 @@ func _on_leaderboard_updated(sorted_scores: Array):
|
||||
})
|
||||
rpc("sync_leaderboard_data", player_data)
|
||||
|
||||
func _on_global_timer_updated(time_remaining: float):
|
||||
"""Update the global match timer display."""
|
||||
var global_timer_panel = get_node_or_null("GlobalMatchTimer")
|
||||
if global_timer_panel:
|
||||
var timer_label = global_timer_panel.get_node_or_null("VBox/TimerLabel")
|
||||
if timer_label:
|
||||
var minutes = int(time_remaining) / 60
|
||||
var seconds = int(time_remaining) % 60
|
||||
timer_label.text = "%d:%02d" % [minutes, seconds]
|
||||
|
||||
func _on_match_ended():
|
||||
"""Called when the global match timer ends - show game over screen."""
|
||||
print("[Main] Match ended! Showing game over screen...")
|
||||
|
||||
# Disable player controls
|
||||
var local_player = GameStateManager.local_player_character
|
||||
if local_player:
|
||||
local_player.action_points = 0
|
||||
|
||||
# Show game over overlay
|
||||
_show_game_over_panel()
|
||||
|
||||
func _show_game_over_panel():
|
||||
"""Create and display the game over panel with final leaderboard."""
|
||||
# Check if panel already exists
|
||||
var existing_panel = get_node_or_null("GameOverPanel")
|
||||
if existing_panel:
|
||||
existing_panel.show()
|
||||
return
|
||||
|
||||
# Create game over panel
|
||||
var panel = PanelContainer.new()
|
||||
panel.name = "GameOverPanel"
|
||||
panel.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
|
||||
# Semi-transparent dark background
|
||||
var style = StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.0, 0.0, 0.0, 0.85)
|
||||
panel.add_theme_stylebox_override("panel", style)
|
||||
|
||||
# Content container
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.name = "VBox"
|
||||
vbox.set_anchors_preset(Control.PRESET_CENTER)
|
||||
vbox.add_theme_constant_override("separation", 20)
|
||||
vbox.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
panel.add_child(vbox)
|
||||
|
||||
# Center the vbox
|
||||
var margin = MarginContainer.new()
|
||||
margin.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
margin.add_theme_constant_override("margin_left", 200)
|
||||
margin.add_theme_constant_override("margin_right", 200)
|
||||
margin.add_theme_constant_override("margin_top", 100)
|
||||
margin.add_theme_constant_override("margin_bottom", 100)
|
||||
panel.add_child(margin)
|
||||
|
||||
var inner_vbox = VBoxContainer.new()
|
||||
inner_vbox.add_theme_constant_override("separation", 30)
|
||||
inner_vbox.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
margin.add_child(inner_vbox)
|
||||
|
||||
# Title
|
||||
var title = Label.new()
|
||||
title.text = "⏱️ TIME'S UP!"
|
||||
title.add_theme_font_size_override("font_size", 64)
|
||||
title.add_theme_color_override("font_color", Color(0.992, 0.796, 0.047))
|
||||
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
inner_vbox.add_child(title)
|
||||
|
||||
# Subtitle
|
||||
var subtitle = Label.new()
|
||||
subtitle.text = "FINAL STANDINGS"
|
||||
subtitle.add_theme_font_size_override("font_size", 24)
|
||||
subtitle.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7))
|
||||
subtitle.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
inner_vbox.add_child(subtitle)
|
||||
|
||||
# Leaderboard container
|
||||
var leaderboard_container = VBoxContainer.new()
|
||||
leaderboard_container.add_theme_constant_override("separation", 15)
|
||||
inner_vbox.add_child(leaderboard_container)
|
||||
|
||||
# Get final scores
|
||||
var player_scores = []
|
||||
for p in get_tree().get_nodes_in_group("Players"):
|
||||
player_scores.append({
|
||||
"name": p.name,
|
||||
"score": goals_cycle_manager.get_player_score(p.get_multiplayer_authority()) if goals_cycle_manager else 0
|
||||
})
|
||||
player_scores.sort_custom(func(a, b): return a.score > b.score)
|
||||
|
||||
# Display each player
|
||||
for i in range(min(player_scores.size(), 4)):
|
||||
var entry = HBoxContainer.new()
|
||||
entry.add_theme_constant_override("separation", 20)
|
||||
|
||||
var rank_colors = [Color(1.0, 0.84, 0.0), Color(0.75, 0.75, 0.75), Color(0.8, 0.5, 0.2), Color(0.5, 0.5, 0.5)]
|
||||
var rank_emojis = ["🥇", "🥈", "🥉", "4th"]
|
||||
|
||||
var rank_label = Label.new()
|
||||
rank_label.text = rank_emojis[i]
|
||||
rank_label.add_theme_font_size_override("font_size", 32)
|
||||
entry.add_child(rank_label)
|
||||
|
||||
var name_label = Label.new()
|
||||
name_label.text = "Player %s" % player_scores[i].name
|
||||
name_label.add_theme_font_size_override("font_size", 28)
|
||||
name_label.add_theme_color_override("font_color", rank_colors[i])
|
||||
name_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
entry.add_child(name_label)
|
||||
|
||||
var score_label = Label.new()
|
||||
score_label.text = str(player_scores[i].score)
|
||||
score_label.add_theme_font_size_override("font_size", 28)
|
||||
score_label.add_theme_color_override("font_color", Color(0.4, 1.0, 0.4))
|
||||
entry.add_child(score_label)
|
||||
|
||||
leaderboard_container.add_child(entry)
|
||||
|
||||
# Back to Menu button
|
||||
var back_btn = Button.new()
|
||||
back_btn.name = "BackToMenuBtn"
|
||||
back_btn.text = "BACK TO MAIN MENU"
|
||||
back_btn.custom_minimum_size = Vector2(300, 60)
|
||||
back_btn.add_theme_font_size_override("font_size", 20)
|
||||
back_btn.pressed.connect(_on_back_to_menu_pressed)
|
||||
inner_vbox.add_child(back_btn)
|
||||
|
||||
# Center the button
|
||||
var btn_container = HBoxContainer.new()
|
||||
btn_container.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
btn_container.add_child(back_btn)
|
||||
inner_vbox.add_child(btn_container)
|
||||
|
||||
add_child(panel)
|
||||
|
||||
func _on_back_to_menu_pressed():
|
||||
"""Return to lobby/main menu and clean up game state."""
|
||||
print("[Main] Returning to lobby...")
|
||||
|
||||
# Clean up game state
|
||||
GameStateManager.end_game()
|
||||
LobbyManager.reset()
|
||||
|
||||
# Go back to lobby
|
||||
if get_tree():
|
||||
get_tree().change_scene_to_file("res://scenes/lobby.tscn")
|
||||
|
||||
func _deferred_init_leaderboard():
|
||||
"""Initialize leaderboard after a delay to ensure all players are loaded."""
|
||||
# Longer delay ensures players are synced
|
||||
|
||||
+88
-82
@@ -23,6 +23,22 @@
|
||||
[ext_resource type="StyleBox" uid="uid://cdhnwvcklbyl8" path="res://assets/styles/ribbon_hovered_gui.tres" id="19_w1rqq"]
|
||||
[ext_resource type="StyleBox" uid="uid://3yog1weaqhxb" path="res://assets/styles/ribbon_unselected_gui.tres" id="20_q6bc1"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerboard"]
|
||||
content_margin_left = 8.0
|
||||
content_margin_top = 8.0
|
||||
content_margin_right = 8.0
|
||||
content_margin_bottom = 8.0
|
||||
bg_color = Color(0.08, 0.08, 0.12, 0.9)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.3, 0.3, 0.4, 0.8)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1cewu"]
|
||||
|
||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_5oeq4"]
|
||||
@@ -30,22 +46,6 @@ texture = ExtResource("13_ahjgs")
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_s1l63"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerboard"]
|
||||
bg_color = Color(0.08, 0.08, 0.12, 0.9)
|
||||
border_color = Color(0.3, 0.3, 0.4, 0.8)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
content_margin_left = 8.0
|
||||
content_margin_top = 8.0
|
||||
content_margin_right = 8.0
|
||||
content_margin_bottom = 8.0
|
||||
|
||||
[node name="Main" type="Node3D"]
|
||||
script = ExtResource("1_xcpe3")
|
||||
|
||||
@@ -77,26 +77,31 @@ fov = 35.5
|
||||
size = 23.0
|
||||
|
||||
[node name="NetworkPanel" type="Panel" parent="."]
|
||||
anchors_preset = 4
|
||||
anchor_top = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = 1208.0
|
||||
offset_top = 280.0
|
||||
offset_right = 1344.0
|
||||
offset_bottom = 333.0
|
||||
grow_vertical = 2
|
||||
anchors_preset = 5
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
offset_left = -185.0
|
||||
offset_top = 25.0
|
||||
offset_right = 185.0
|
||||
offset_bottom = 78.0
|
||||
grow_horizontal = 2
|
||||
theme_override_styles/panel = ExtResource("5_dvx6y")
|
||||
|
||||
[node name="NetworkInfo" type="VBoxContainer" parent="NetworkPanel"]
|
||||
layout_mode = 0
|
||||
offset_left = 8.0
|
||||
offset_right = 124.0
|
||||
offset_bottom = 50.0
|
||||
[node name="NetworkInfo" type="HBoxContainer" parent="NetworkPanel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 50
|
||||
alignment = 1
|
||||
|
||||
[node name="NetworkSideDisplay" type="Label" parent="NetworkPanel/NetworkInfo"]
|
||||
layout_mode = 2
|
||||
text = "Network Side"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="UniquePeerID" type="Label" parent="NetworkPanel/NetworkInfo"]
|
||||
layout_mode = 2
|
||||
@@ -108,10 +113,10 @@ vertical_alignment = 1
|
||||
anchors_preset = 4
|
||||
anchor_top = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = 10.0
|
||||
offset_top = -120.0
|
||||
offset_right = 216.0
|
||||
offset_bottom = 120.0
|
||||
offset_left = 19.0
|
||||
offset_top = -233.0
|
||||
offset_right = 232.0
|
||||
offset_bottom = 7.0
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_playerboard")
|
||||
|
||||
@@ -120,10 +125,10 @@ clip_contents = true
|
||||
anchors_preset = 4
|
||||
anchor_top = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = 18.0
|
||||
offset_top = -88.0
|
||||
offset_right = 208.0
|
||||
offset_bottom = 102.0
|
||||
offset_left = 28.0
|
||||
offset_top = -199.0
|
||||
offset_right = 224.0
|
||||
offset_bottom = -3.0
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
columns = 5
|
||||
@@ -978,7 +983,43 @@ grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = ExtResource("9_6gcb6")
|
||||
|
||||
[node name="PowerUpBar" type="PanelContainer" parent="."]
|
||||
anchors_preset = 4
|
||||
anchor_top = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = 29.0
|
||||
offset_top = -228.0
|
||||
offset_right = 149.0
|
||||
offset_bottom = -202.0
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="PowerUpBar"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="PowerLabel" type="Label" parent="PowerUpBar/HBox"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "⚡"
|
||||
|
||||
[node name="Segment0" type="Panel" parent="PowerUpBar/HBox"]
|
||||
custom_minimum_size = Vector2(20, 16)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Segment1" type="Panel" parent="PowerUpBar/HBox"]
|
||||
custom_minimum_size = Vector2(20, 16)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Segment2" type="Panel" parent="PowerUpBar/HBox"]
|
||||
custom_minimum_size = Vector2(20, 16)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Segment3" type="Panel" parent="PowerUpBar/HBox"]
|
||||
custom_minimum_size = Vector2(20, 16)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ActionMenu" type="Control" parent="."]
|
||||
visible = false
|
||||
layout_mode = 3
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
@@ -9334,9 +9375,9 @@ anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -200.0
|
||||
offset_top = -99.0
|
||||
offset_top = -87.0
|
||||
offset_right = 200.0
|
||||
offset_bottom = -41.0
|
||||
offset_bottom = -53.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
theme_override_styles/panel = ExtResource("5_dvx6y")
|
||||
@@ -9352,57 +9393,22 @@ theme_override_constants/margin_bottom = 5
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="PowerUpBar" type="PanelContainer" parent="."]
|
||||
anchors_preset = 4
|
||||
anchor_top = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = 18.0
|
||||
offset_top = -115.0
|
||||
offset_right = 110.0
|
||||
offset_bottom = -92.0
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="PowerUpBar"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="PowerLabel" type="Label" parent="PowerUpBar/HBox"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "⚡"
|
||||
|
||||
[node name="Segment0" type="Panel" parent="PowerUpBar/HBox"]
|
||||
custom_minimum_size = Vector2(20, 16)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Segment1" type="Panel" parent="PowerUpBar/HBox"]
|
||||
custom_minimum_size = Vector2(20, 16)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Segment2" type="Panel" parent="PowerUpBar/HBox"]
|
||||
custom_minimum_size = Vector2(20, 16)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Segment3" type="Panel" parent="PowerUpBar/HBox"]
|
||||
custom_minimum_size = Vector2(20, 16)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LeaderboardPanel" type="PanelContainer" parent="."]
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -210.0
|
||||
offset_top = 80.0
|
||||
offset_right = -10.0
|
||||
offset_bottom = 280.0
|
||||
offset_left = -218.0
|
||||
offset_top = 15.0
|
||||
offset_right = -18.0
|
||||
offset_bottom = 215.0
|
||||
grow_horizontal = 0
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="LeaderboardPanel"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 8
|
||||
theme_override_constants/margin_top = 8
|
||||
theme_override_constants/margin_right = 8
|
||||
theme_override_constants/margin_bottom = 8
|
||||
theme_override_constants/margin_left = 18
|
||||
theme_override_constants/margin_top = 18
|
||||
theme_override_constants/margin_right = 18
|
||||
theme_override_constants/margin_bottom = 18
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="LeaderboardPanel/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
Reference in New Issue
Block a user