feat: Introduce modular player system with dedicated managers for movement, race, input, playerboard, actions, special tiles, and powerups, along with a new main scene and documentation.

This commit is contained in:
2025-12-16 02:37:26 +08:00
parent 96f5754f99
commit e41ffcfb67
9 changed files with 494 additions and 146 deletions
+116 -18
View File
@@ -267,6 +267,11 @@ func _start_game():
# Start the goals cycle timer
if goals_cycle_manager:
goals_cycle_manager.start_cycle()
# Initialize leaderboard with all players
if ui_manager:
var all_players = get_tree().get_nodes_in_group("Players")
ui_manager.initialize_leaderboard_with_players(all_players)
# =============================================================================
# Player Management
@@ -378,6 +383,9 @@ func sync_game_start(player_list: Array, is_turn_based: bool):
GameStateManager.players = player_list
TurnManager.turn_based_mode = is_turn_based
GameStateManager.start_game()
# Initialize leaderboard for all peers (after a delay to ensure players loaded)
call_deferred("_deferred_init_leaderboard")
# =============================================================================
# UI / Action State Management
@@ -731,15 +739,14 @@ func sync_grid_item(x: int, y: int, z: int, item: int):
# =============================================================================
func _on_timer_updated(time_remaining: float):
# Update timer display on all player goal panels
var time_text = "%02d:%02d" % [int(time_remaining) / 60, int(time_remaining) % 60]
# Update standalone timer display
var time_text = str(int(time_remaining))
for i in range($AllPlayerGoals.get_child_count()):
var panel = $AllPlayerGoals.get_child(i)
if panel.visible:
var timer_label = panel.get_node_or_null("TimerLabel")
if timer_label:
timer_label.text = time_text
var timer_panel = get_node_or_null("GoalsTimer")
if timer_panel:
var timer_label = timer_panel.get_node_or_null("VBox/TimerLabel")
if timer_label:
timer_label.text = time_text
func _on_score_updated(peer_id: int, new_score: int):
# Update player's score display
@@ -751,26 +758,117 @@ func _on_score_updated(peer_id: int, new_score: int):
_update_leaderboard_display()
func _on_leaderboard_updated(sorted_scores: Array):
# Update the leaderboard panel
# Update the leaderboard panel locally
_update_leaderboard_display()
# Server broadcasts updated leaderboard to all clients
if multiplayer.is_server():
var player_data = []
for p in get_tree().get_nodes_in_group("Players"):
player_data.append({
"peer_id": p.get_multiplayer_authority(),
"name": p.name,
"score": goals_cycle_manager.get_player_score(p.get_multiplayer_authority()) if goals_cycle_manager else 0
})
rpc("sync_leaderboard_data", player_data)
func _deferred_init_leaderboard():
"""Initialize leaderboard after a delay to ensure all players are loaded."""
# Longer delay ensures players are synced
await get_tree().create_timer(1.5).timeout
# Request leaderboard sync from server for accurate data
if not multiplayer.is_server():
rpc_id(1, "request_leaderboard_sync")
else:
# Server can update directly
_update_leaderboard_display()
@rpc("any_peer")
func request_leaderboard_sync():
"""Client requests leaderboard data from server."""
if multiplayer.is_server():
var sender_id = multiplayer.get_remote_sender_id()
# Build player list with peer_ids and names
var player_data = []
for p in get_tree().get_nodes_in_group("Players"):
player_data.append({
"peer_id": p.get_multiplayer_authority(),
"name": p.name,
"score": goals_cycle_manager.get_player_score(p.get_multiplayer_authority()) if goals_cycle_manager else 0
})
rpc_id(sender_id, "sync_leaderboard_data", player_data)
@rpc("authority", "call_local", "reliable")
func sync_leaderboard_data(player_data: Array):
"""Receive leaderboard data from server and update UI."""
var leaderboard_panel = get_node_or_null("LeaderboardPanel")
if not leaderboard_panel:
return
var vbox = leaderboard_panel.get_node_or_null("MarginContainer/VBox")
if not vbox:
return
# Sort by score descending
player_data.sort_custom(func(a, b): return a.score > b.score)
# Update entries
for i in range(4):
var entry = vbox.get_node_or_null("Entry" + str(i + 1))
if not entry:
continue
if i < player_data.size():
var data = player_data[i]
var rank_label = entry.get_node_or_null("RankLabel")
var name_label = entry.get_node_or_null("NameLabel")
var score_label = entry.get_node_or_null("ScoreLabel")
if rank_label:
rank_label.text = _get_ordinal(i + 1)
if name_label:
name_label.text = "Player " + str(data.name)
if score_label:
score_label.text = str(data.score)
entry.visible = true
else:
entry.visible = false
func _update_leaderboard_display():
var leaderboard_panel = get_node_or_null("LeaderboardPanel")
if not leaderboard_panel:
return
var sorted_scores = goals_cycle_manager.get_leaderboard() if goals_cycle_manager else []
# Try both possible paths for vbox
var vbox = leaderboard_panel.get_node_or_null("MarginContainer/VBox")
if not vbox:
vbox = leaderboard_panel.get_node_or_null("VBox")
if not vbox:
return
# Get player names and update entries
# Get all players in game
var all_players = get_tree().get_nodes_in_group("Players")
# Build scores array with all players
var player_data = []
for p in all_players:
var peer_id = p.get_multiplayer_authority()
var score = goals_cycle_manager.get_player_score(peer_id) if goals_cycle_manager else 0
player_data.append({"peer_id": peer_id, "name": p.name, "score": score})
# Sort by score descending
player_data.sort_custom(func(a, b): return a.score > b.score)
# Update entries
for i in range(4): # Max 4 entries
var entry = leaderboard_panel.get_node_or_null("Entry" + str(i + 1))
var entry = vbox.get_node_or_null("Entry" + str(i + 1))
if not entry:
continue
if i < sorted_scores.size():
var score_data = sorted_scores[i]
var player = get_node_or_null(str(score_data.peer_id))
var player_name = player.name if player else str(score_data.peer_id)
if i < player_data.size():
var data = player_data[i]
var rank_label = entry.get_node_or_null("RankLabel")
var name_label = entry.get_node_or_null("NameLabel")
@@ -779,9 +877,9 @@ func _update_leaderboard_display():
if rank_label:
rank_label.text = _get_ordinal(i + 1)
if name_label:
name_label.text = player_name
name_label.text = str(data.name)
if score_label:
score_label.text = str(score_data.score)
score_label.text = str(data.score)
entry.visible = true
else: