feat: Implement Nakama serialization, Realtime API, and a comprehensive lobby system with UI and player management.
This commit is contained in:
+107
-31
@@ -370,6 +370,9 @@ func _setup_host_game():
|
||||
if touch_controls:
|
||||
touch_controls.set_player(player_character)
|
||||
|
||||
# Set host name
|
||||
player_character.display_name = LobbyManager.local_player_name
|
||||
|
||||
# Spawn client players that joined via lobby (need to add them first)
|
||||
var lobby_players = LobbyManager.get_players()
|
||||
for lobby_player in lobby_players:
|
||||
@@ -429,6 +432,13 @@ func _spawn_lobby_client_sync(peer_id: int):
|
||||
player_character.add_to_group("Players", true)
|
||||
GameStateManager.add_player(peer_id)
|
||||
|
||||
# Set name from LobbyManager data if available
|
||||
var lobby_players = LobbyManager.get_players()
|
||||
for p_data in lobby_players:
|
||||
if p_data.get("id") == peer_id:
|
||||
player_character.display_name = p_data.get("name", "Player")
|
||||
break
|
||||
|
||||
# Tell all clients to create this player
|
||||
rpc("add_newly_connected_player_character", peer_id)
|
||||
|
||||
@@ -529,43 +539,93 @@ func _start_game():
|
||||
ui_manager.initialize_leaderboard_with_players(all_players)
|
||||
|
||||
func _assign_random_spawn_positions():
|
||||
"""Assign random unique spawn positions to all players."""
|
||||
# Fetch all valid walkable positions from the generated map
|
||||
var valid_spawns = []
|
||||
"""Assign spawn positions distributed to 4 corners (2 per corner for 8 players)."""
|
||||
var enhanced_gridmap = $EnhancedGridMap
|
||||
|
||||
if enhanced_gridmap:
|
||||
for x in range(enhanced_gridmap.columns):
|
||||
for z in range(enhanced_gridmap.rows):
|
||||
var ground = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||
if ground == 0: # Walkable
|
||||
valid_spawns.append(Vector2i(x, z))
|
||||
|
||||
# Fallback if map generation failed or is empty
|
||||
if valid_spawns.size() < 12:
|
||||
print("Warning: Low spawn count! Adding defaults.")
|
||||
for i in range(12):
|
||||
if not Vector2i(0, i) in valid_spawns:
|
||||
valid_spawns.append(Vector2i(0, i))
|
||||
if not enhanced_gridmap:
|
||||
return
|
||||
|
||||
# Shuffle spawn locations
|
||||
valid_spawns.shuffle()
|
||||
# Lists for each quadrant
|
||||
var spawns_TL = [] # Top-Left
|
||||
var spawns_TR = [] # Top-Right
|
||||
var spawns_BL = [] # Bottom-Left
|
||||
var spawns_BR = [] # Bottom-Right
|
||||
var all_spawns = [] # Fallback
|
||||
|
||||
var mid_x = enhanced_gridmap.columns / 2
|
||||
var mid_z = enhanced_gridmap.rows / 2
|
||||
|
||||
for x in range(enhanced_gridmap.columns):
|
||||
for z in range(enhanced_gridmap.rows):
|
||||
var ground = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||
if ground == 0: # Walkable
|
||||
var pos = Vector2i(x, z)
|
||||
all_spawns.append(pos)
|
||||
|
||||
if x < mid_x and z < mid_z:
|
||||
spawns_TL.append(pos)
|
||||
elif x >= mid_x and z < mid_z:
|
||||
spawns_TR.append(pos)
|
||||
elif x < mid_x and z >= mid_z:
|
||||
spawns_BL.append(pos)
|
||||
else:
|
||||
spawns_BR.append(pos)
|
||||
|
||||
# Sort lists by distance to corners (closest to corner should be last, to be popped first)
|
||||
# TL: Close to (0,0) -> Sort descending distance (so closest is at end)
|
||||
spawns_TL.sort_custom(func(a, b): return a.length_squared() > b.length_squared())
|
||||
|
||||
# TR: Close to (13, 0)
|
||||
var tr_corner = Vector2i(enhanced_gridmap.columns - 1, 0)
|
||||
spawns_TR.sort_custom(func(a, b): return a.distance_squared_to(tr_corner) > b.distance_squared_to(tr_corner))
|
||||
|
||||
# BL: Close to (0, 13)
|
||||
var bl_corner = Vector2i(0, enhanced_gridmap.rows - 1)
|
||||
spawns_BL.sort_custom(func(a, b): return a.distance_squared_to(bl_corner) > b.distance_squared_to(bl_corner))
|
||||
|
||||
# BR: Close to (13, 13)
|
||||
var br_corner = Vector2i(enhanced_gridmap.columns - 1, enhanced_gridmap.rows - 1)
|
||||
spawns_BR.sort_custom(func(a, b): return a.distance_squared_to(br_corner) > b.distance_squared_to(br_corner))
|
||||
|
||||
# Fallback shuffle
|
||||
all_spawns.shuffle()
|
||||
|
||||
# Get all players
|
||||
var all_players = get_tree().get_nodes_in_group("Players")
|
||||
|
||||
# Assign positions
|
||||
var spawn_index = 0
|
||||
|
||||
# Round-robin assignment to corners: TL, TR, BR, BL, TL, TR, BR, BL...
|
||||
# Order: TL -> TR -> BR -> BL (Clockwise-ish)
|
||||
var quadrants = [spawns_TL, spawns_TR, spawns_BR, spawns_BL]
|
||||
|
||||
for player in all_players:
|
||||
if spawn_index >= valid_spawns.size():
|
||||
print("Critical: Not enough spawn points for players!")
|
||||
break
|
||||
var spawn_pos = valid_spawns[spawn_index]
|
||||
# Set position and sync to all clients
|
||||
player.current_position = spawn_pos
|
||||
player.position = player.grid_to_world(spawn_pos)
|
||||
player.spawn_point_selected = true
|
||||
player.rpc("set_spawn_position", spawn_pos)
|
||||
var assigned_pos = Vector2i(-1, -1)
|
||||
|
||||
# Try to get from the current quadrant
|
||||
var quadrant_idx = spawn_index % 4
|
||||
var quadrant = quadrants[quadrant_idx]
|
||||
|
||||
if quadrant.size() > 0:
|
||||
assigned_pos = quadrant.pop_back()
|
||||
else:
|
||||
# Fallback: Try other quadrants if preferred one is empty
|
||||
for q in quadrants:
|
||||
if q.size() > 0:
|
||||
assigned_pos = q.pop_back()
|
||||
break
|
||||
|
||||
# Ultimate fallback: Random from anywhere
|
||||
if assigned_pos == Vector2i(-1, -1) and all_spawns.size() > 0:
|
||||
assigned_pos = all_spawns.pop_back()
|
||||
|
||||
if assigned_pos != Vector2i(-1, -1):
|
||||
# Set position and sync to all clients
|
||||
player.current_position = assigned_pos
|
||||
player.position = player.grid_to_world(assigned_pos)
|
||||
player.spawn_point_selected = true
|
||||
player.rpc("set_spawn_position", assigned_pos)
|
||||
else:
|
||||
print("Critical: No spawn point found for player ", player.name)
|
||||
|
||||
spawn_index += 1
|
||||
|
||||
# =============================================================================
|
||||
@@ -910,9 +970,11 @@ func request_full_player_sync(requesting_peer_id: int):
|
||||
var player_data = {
|
||||
"peer_id": peer_id,
|
||||
"position": player.current_position,
|
||||
"name": player.display_name,
|
||||
"goals": player.goals,
|
||||
"playerboard": player.playerboard,
|
||||
"is_bot": player.is_bot || player.is_in_group("Bots")
|
||||
"is_bot": player.is_bot || player.is_in_group("Bots"),
|
||||
"spawn_point_selected": player.spawn_point_selected
|
||||
}
|
||||
rpc_id(requesting_peer_id, "create_specific_player", player_data)
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
@@ -933,6 +995,20 @@ 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"]
|
||||
|
||||
if data["is_bot"]:
|
||||
player_character.add_to_group("Bots", true)
|
||||
player_character.is_bot = true
|
||||
|
||||
Reference in New Issue
Block a user