From 0c2a53c0cc8b98dd58a0527fa9f73bec98414e7e Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Tue, 10 Feb 2026 16:58:43 +0800 Subject: [PATCH] feat: Introduce LobbyManager to handle room management, player states, and game setting synchronization. --- scenes/main.gd | 51 ++++++++++++++++++++----------- scenes/player.gd | 42 +++++++++---------------- scripts/managers/lobby_manager.gd | 2 +- 3 files changed, 49 insertions(+), 46 deletions(-) diff --git a/scenes/main.gd b/scenes/main.gd index eba0b42..5ff17fd 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -45,6 +45,7 @@ func _ready(): if message_bar: message_bar.visible = false + # Ensure grid is randomized with Scarcity if server if multiplayer.is_server(): randomize_game_grid() @@ -57,6 +58,11 @@ func _ready(): respawn_check_timer.autostart = true respawn_check_timer.timeout.connect(_check_respawns) add_child(respawn_check_timer) + + # Force gridmap cell size to match player logic (1, 0.05, 1) - >0.001 to avoid errors + var em = $EnhancedGridMap + if em: + em.cell_size = Vector3(1, 0.05, 1) func _on_goal_count_updated(peer_id: int, count: int): # Only update for local player @@ -392,8 +398,9 @@ func _setup_host_game(): # IMMEDIATELY assign random spawn positions before any player _ready() completes # Player _ready() has 0.1s await, so we assign before that completes - if LobbyManager.get_randomize_spawn(): - _assign_random_spawn_positions() + # IMMEDIATELY assign spawn positions (Fixed or Random) + # Always run this to ensure players don't spawn at (0,0) overlap + _assign_random_spawn_positions() # Wait for players to be fully ready (player.gd has 0.1s await in _ready before managers init) await get_tree().create_timer(0.3).timeout @@ -598,8 +605,10 @@ func _assign_random_spawn_positions(): # Fallback shuffle all_spawns.shuffle() - # Get all players + # Get all players and sort them for deterministic assignment var all_players = get_tree().get_nodes_in_group("Players") + all_players.sort_custom(func(a, b): return a.name.to_int() < b.name.to_int()) + var spawn_index = 0 # Round-robin assignment to corners: TL, TR, BR, BL, TL, TR, BR, BL... @@ -670,7 +679,9 @@ func create_bot(bot_id: int): @rpc("any_peer", "call_local") func add_player_character(peer_id: int, is_bot: bool = false): + print("[Main] add_player_character called for %d (is_bot: %s)" % [peer_id, is_bot]) if has_node(str(peer_id)): + print("[Main] Player %d already exists! Skipping spawn." % peer_id) return var player_character @@ -1013,16 +1024,6 @@ func create_specific_player(data: Dictionary): add_child(player_character) player_character.add_to_group("Players", true) - # Set spawn flag directly so it doesn't hide itself - if data.has("spawn_point_selected") and data["spawn_point_selected"]: - player_character.spawn_point_selected = true - player_character.visible = true - - # Ensure visual position matches logical - var new_pos = player_character.grid_to_world(data["position"]) - player_character.global_position = new_pos - player_character.target_visual_position = new_pos - # Set display name if data.has("name"): player_character.display_name = data["name"] @@ -1030,6 +1031,20 @@ func create_specific_player(data: Dictionary): if data["is_bot"]: player_character.add_to_group("Bots", true) player_character.is_bot = true + + # Set spawn flag and visibility for BOTH new and existing players + if data.has("spawn_point_selected") and data["spawn_point_selected"]: + player_character.spawn_point_selected = true + player_character.visible = true + + # Ensure visual position matches logical + var new_pos = player_character.grid_to_world(data["position"]) + player_character.global_position = new_pos + player_character.target_visual_position = new_pos + + # Force collision update + if player_character.has_node("CollisionShape3D"): + player_character.get_node("CollisionShape3D").disabled = false # Check if this is the local player (client's own player) var is_local_player = (peer_id == multiplayer.get_unique_id()) @@ -1052,11 +1067,11 @@ func create_specific_player(data: Dictionary): # Always update position (including for existing nodes, so client sees host correctly) player_character.current_position = data["position"] - player_character.global_position = Vector3( - data["position"].x * 2 + 1, - 1.0, - data["position"].y * 2 + 1 - ) + + # Use the player's own grid_to_world to respect cell_size (1,1,1) + var new_world_pos = player_character.grid_to_world(data["position"]) + player_character.global_position = new_world_pos + player_character.target_visual_position = new_world_pos # Update playerboard UI for local player if is_local_player: diff --git a/scenes/player.gd b/scenes/player.gd index 1966279..89135d2 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -70,7 +70,7 @@ var can_finish: bool: get: return race_manager.can_finish if race_manager else false set(value): if race_manager: race_manager.can_finish = value -@export var cell_size: Vector3 = Vector3(2, 2, 2) +@export var cell_size: Vector3 = Vector3(1, 0.05, 1) @export var cell_offset: Vector3 = Vector3(0, 0, 0) @export var goals: Array[int]: @@ -268,9 +268,9 @@ func _ready(): # Ensure proper initial positioning if not LobbyManager.get_randomize_spawn() and not spawn_point_selected: global_position = Vector3( - current_position.x * cell_size.x + cell_size.x * 0.5, + current_position.x * 1 + 1 * 0.5, 1.0, - current_position.y * cell_size.z + cell_size.z * 0.5 + current_position.y * 1 + 1 * 0.5 ) target_visual_position = global_position if is_multiplayer_authority(): @@ -1011,31 +1011,17 @@ func find_valid_starting_position() -> Vector2i: if is_bot: return _find_random_spawn_position() else: - highlight_available_spawn_points() - # Return temporary position, will be updated when player selects spawn point - return Vector2i(-1, -1) - -func highlight_available_spawn_points(): - if not is_multiplayer_authority() or is_bot or spawn_point_selected: - return + # Auto-assign the first available spawn point for fixed spawning + for spawn_pos in spawn_locations: + if not is_position_occupied(spawn_pos): + return spawn_pos - # Clear any existing highlights - clear_highlights() - highlighted_spawn_points.clear() - - # Get all currently occupied positions - var occupied_positions = get_occupied_positions() - - # Check each spawn location - for spawn_pos in spawn_locations: - if not is_position_occupied(spawn_pos): - highlighted_spawn_points.append(spawn_pos) - if enhanced_gridmap: - # Highlight the cell at y=0 (ground level) - enhanced_gridmap.set_cell_item( - Vector3i(spawn_pos.x, 0, spawn_pos.y), - enhanced_gridmap.hover_item - ) + # Fallback (should typically not be reached if spawn_locations > max_players) + return Vector2i(0, 0) + +# highlight_available_spawn_points is no longer needed for manual selection in this mode +func highlight_available_spawn_points(): + pass # Add function to get all occupied positions func get_occupied_positions() -> Array: @@ -1808,6 +1794,8 @@ func set_spawn_position(pos: Vector2i): current_position.y * cell_size.z + cell_size.z * 0.5 ) + cell_offset + print("[Player %s] set_spawn_position: Grid %s -> World %s (CellSize: %s)" % [name, pos, new_pos, cell_size]) + global_position = new_pos target_visual_position = new_pos diff --git a/scripts/managers/lobby_manager.gd b/scripts/managers/lobby_manager.gd index 9acaac1..0eee065 100644 --- a/scripts/managers/lobby_manager.gd +++ b/scripts/managers/lobby_manager.gd @@ -28,7 +28,7 @@ var local_player_name: String = "Player" var match_duration: int = 180 # Default 3 minutes # Randomize spawn locations (configurable in lobby by host) -var randomize_spawn: bool = true # Default enabled +var randomize_spawn: bool = false # Default enabled # Timer setting var enable_cycle_timer: bool = false # Default disabled