feat: Introduce LobbyManager to handle room management, player states, and game setting synchronization.
This commit is contained in:
+33
-18
@@ -45,6 +45,7 @@ func _ready():
|
|||||||
if message_bar:
|
if message_bar:
|
||||||
message_bar.visible = false
|
message_bar.visible = false
|
||||||
|
|
||||||
|
|
||||||
# Ensure grid is randomized with Scarcity if server
|
# Ensure grid is randomized with Scarcity if server
|
||||||
if multiplayer.is_server():
|
if multiplayer.is_server():
|
||||||
randomize_game_grid()
|
randomize_game_grid()
|
||||||
@@ -58,6 +59,11 @@ func _ready():
|
|||||||
respawn_check_timer.timeout.connect(_check_respawns)
|
respawn_check_timer.timeout.connect(_check_respawns)
|
||||||
add_child(respawn_check_timer)
|
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):
|
func _on_goal_count_updated(peer_id: int, count: int):
|
||||||
# Only update for local player
|
# Only update for local player
|
||||||
if peer_id == multiplayer.get_unique_id():
|
if peer_id == multiplayer.get_unique_id():
|
||||||
@@ -392,8 +398,9 @@ func _setup_host_game():
|
|||||||
|
|
||||||
# IMMEDIATELY assign random spawn positions before any player _ready() completes
|
# IMMEDIATELY assign random spawn positions before any player _ready() completes
|
||||||
# Player _ready() has 0.1s await, so we assign before that completes
|
# Player _ready() has 0.1s await, so we assign before that completes
|
||||||
if LobbyManager.get_randomize_spawn():
|
# IMMEDIATELY assign spawn positions (Fixed or Random)
|
||||||
_assign_random_spawn_positions()
|
# 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)
|
# 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
|
await get_tree().create_timer(0.3).timeout
|
||||||
@@ -598,8 +605,10 @@ func _assign_random_spawn_positions():
|
|||||||
# Fallback shuffle
|
# Fallback shuffle
|
||||||
all_spawns.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")
|
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
|
var spawn_index = 0
|
||||||
|
|
||||||
# Round-robin assignment to corners: TL, TR, BR, BL, TL, TR, BR, BL...
|
# 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")
|
@rpc("any_peer", "call_local")
|
||||||
func add_player_character(peer_id: int, is_bot: bool = false):
|
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)):
|
if has_node(str(peer_id)):
|
||||||
|
print("[Main] Player %d already exists! Skipping spawn." % peer_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
var player_character
|
var player_character
|
||||||
@@ -1013,16 +1024,6 @@ func create_specific_player(data: Dictionary):
|
|||||||
add_child(player_character)
|
add_child(player_character)
|
||||||
player_character.add_to_group("Players", true)
|
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
|
# Set display name
|
||||||
if data.has("name"):
|
if data.has("name"):
|
||||||
player_character.display_name = data["name"]
|
player_character.display_name = data["name"]
|
||||||
@@ -1031,6 +1032,20 @@ func create_specific_player(data: Dictionary):
|
|||||||
player_character.add_to_group("Bots", true)
|
player_character.add_to_group("Bots", true)
|
||||||
player_character.is_bot = 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)
|
# Check if this is the local player (client's own player)
|
||||||
var is_local_player = (peer_id == multiplayer.get_unique_id())
|
var is_local_player = (peer_id == multiplayer.get_unique_id())
|
||||||
if is_local_player and GameStateManager.local_player_character == null:
|
if is_local_player and GameStateManager.local_player_character == null:
|
||||||
@@ -1052,11 +1067,11 @@ func create_specific_player(data: Dictionary):
|
|||||||
|
|
||||||
# Always update position (including for existing nodes, so client sees host correctly)
|
# Always update position (including for existing nodes, so client sees host correctly)
|
||||||
player_character.current_position = data["position"]
|
player_character.current_position = data["position"]
|
||||||
player_character.global_position = Vector3(
|
|
||||||
data["position"].x * 2 + 1,
|
# Use the player's own grid_to_world to respect cell_size (1,1,1)
|
||||||
1.0,
|
var new_world_pos = player_character.grid_to_world(data["position"])
|
||||||
data["position"].y * 2 + 1
|
player_character.global_position = new_world_pos
|
||||||
)
|
player_character.target_visual_position = new_world_pos
|
||||||
|
|
||||||
# Update playerboard UI for local player
|
# Update playerboard UI for local player
|
||||||
if is_local_player:
|
if is_local_player:
|
||||||
|
|||||||
+14
-26
@@ -70,7 +70,7 @@ var can_finish: bool:
|
|||||||
get: return race_manager.can_finish if race_manager else false
|
get: return race_manager.can_finish if race_manager else false
|
||||||
set(value): if race_manager: race_manager.can_finish = value
|
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 cell_offset: Vector3 = Vector3(0, 0, 0)
|
||||||
|
|
||||||
@export var goals: Array[int]:
|
@export var goals: Array[int]:
|
||||||
@@ -268,9 +268,9 @@ func _ready():
|
|||||||
# Ensure proper initial positioning
|
# Ensure proper initial positioning
|
||||||
if not LobbyManager.get_randomize_spawn() and not spawn_point_selected:
|
if not LobbyManager.get_randomize_spawn() and not spawn_point_selected:
|
||||||
global_position = Vector3(
|
global_position = Vector3(
|
||||||
current_position.x * cell_size.x + cell_size.x * 0.5,
|
current_position.x * 1 + 1 * 0.5,
|
||||||
1.0,
|
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
|
target_visual_position = global_position
|
||||||
if is_multiplayer_authority():
|
if is_multiplayer_authority():
|
||||||
@@ -1011,31 +1011,17 @@ func find_valid_starting_position() -> Vector2i:
|
|||||||
if is_bot:
|
if is_bot:
|
||||||
return _find_random_spawn_position()
|
return _find_random_spawn_position()
|
||||||
else:
|
else:
|
||||||
highlight_available_spawn_points()
|
# Auto-assign the first available spawn point for fixed spawning
|
||||||
# Return temporary position, will be updated when player selects spawn point
|
for spawn_pos in spawn_locations:
|
||||||
return Vector2i(-1, -1)
|
if not is_position_occupied(spawn_pos):
|
||||||
|
return spawn_pos
|
||||||
|
|
||||||
|
# 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():
|
func highlight_available_spawn_points():
|
||||||
if not is_multiplayer_authority() or is_bot or spawn_point_selected:
|
pass
|
||||||
return
|
|
||||||
|
|
||||||
# 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
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add function to get all occupied positions
|
# Add function to get all occupied positions
|
||||||
func get_occupied_positions() -> Array:
|
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
|
current_position.y * cell_size.z + cell_size.z * 0.5
|
||||||
) + cell_offset
|
) + cell_offset
|
||||||
|
|
||||||
|
print("[Player %s] set_spawn_position: Grid %s -> World %s (CellSize: %s)" % [name, pos, new_pos, cell_size])
|
||||||
|
|
||||||
global_position = new_pos
|
global_position = new_pos
|
||||||
target_visual_position = new_pos
|
target_visual_position = new_pos
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ var local_player_name: String = "Player"
|
|||||||
var match_duration: int = 180 # Default 3 minutes
|
var match_duration: int = 180 # Default 3 minutes
|
||||||
|
|
||||||
# Randomize spawn locations (configurable in lobby by host)
|
# Randomize spawn locations (configurable in lobby by host)
|
||||||
var randomize_spawn: bool = true # Default enabled
|
var randomize_spawn: bool = false # Default enabled
|
||||||
|
|
||||||
# Timer setting
|
# Timer setting
|
||||||
var enable_cycle_timer: bool = false # Default disabled
|
var enable_cycle_timer: bool = false # Default disabled
|
||||||
|
|||||||
Reference in New Issue
Block a user