feat: Implement Stop N Go game mode with phase management, mission objectives, and visual HUD elements.
This commit is contained in:
+65
-29
@@ -1766,6 +1766,13 @@ func _show_game_over_panel():
|
||||
if existing_panel:
|
||||
existing_panel.show()
|
||||
return
|
||||
|
||||
# Hide Gameplay UI
|
||||
var actions_btn = get_node_or_null("TouchControls/TouchControls/ActionsBtn")
|
||||
if actions_btn: actions_btn.hide()
|
||||
|
||||
if stop_n_go_manager and stop_n_go_manager.hud_layer:
|
||||
stop_n_go_manager.hud_layer.hide()
|
||||
|
||||
# Create game over panel
|
||||
var panel = PanelContainer.new()
|
||||
@@ -1839,41 +1846,78 @@ func _show_game_over_panel():
|
||||
player_scores.sort_custom(func(a, b): return a.score > b.score)
|
||||
|
||||
# Display each player
|
||||
for i in range(min(player_scores.size(), 8)):
|
||||
var local_peer_id = multiplayer.get_unique_id()
|
||||
var local_player_rank = -1
|
||||
|
||||
# Find local player rank in sorted list
|
||||
for i in range(player_scores.size()):
|
||||
if player_scores[i].peer_id == local_peer_id:
|
||||
local_player_rank = i
|
||||
break
|
||||
|
||||
var rank_colors = [
|
||||
Color(1.0, 0.84, 0.0), # Gold
|
||||
Color(0.75, 0.75, 0.75), # Silver
|
||||
Color(0.8, 0.5, 0.2) # Bronze
|
||||
]
|
||||
var rank_emojis = ["🥇", "🥈", "🥉"]
|
||||
|
||||
# Helper to create a leaderboard entry HBox
|
||||
var create_entry = func(rank_idx: int):
|
||||
var entry = HBoxContainer.new()
|
||||
entry.add_theme_constant_override("separation", 20)
|
||||
|
||||
var rank_colors = [
|
||||
Color(1.0, 0.84, 0.0), # Gold
|
||||
Color(0.75, 0.75, 0.75), # Silver
|
||||
Color(0.8, 0.5, 0.2), # Bronze
|
||||
Color(0.5, 0.5, 0.5), # 4th
|
||||
Color(0.5, 0.5, 0.5), # 5th
|
||||
Color(0.5, 0.5, 0.5), # 6th
|
||||
Color(0.5, 0.5, 0.5), # 7th
|
||||
Color(0.5, 0.5, 0.5) # 8th
|
||||
]
|
||||
var rank_emojis = ["🥇", "🥈", "🥉", "4th", "5th", "6th", "7th", "8th"]
|
||||
var score_data = player_scores[rank_idx]
|
||||
var is_local = score_data.peer_id == local_peer_id
|
||||
|
||||
var rank_label = Label.new()
|
||||
rank_label.text = rank_emojis[i]
|
||||
if rank_idx < 3:
|
||||
rank_label.text = rank_emojis[rank_idx]
|
||||
else:
|
||||
# Ordinal rank (4th, 5th, etc.)
|
||||
var n = rank_idx + 1
|
||||
var suffix = "th"
|
||||
if n % 10 == 1 and n % 100 != 11: suffix = "st"
|
||||
elif n % 10 == 2 and n % 100 != 12: suffix = "nd"
|
||||
elif n % 10 == 3 and n % 100 != 13: suffix = "rd"
|
||||
rank_label.text = str(n) + suffix
|
||||
|
||||
rank_label.add_theme_font_size_override("font_size", 32)
|
||||
entry.add_child(rank_label)
|
||||
|
||||
var name_label = Label.new()
|
||||
name_label.text = player_scores[i].name
|
||||
name_label.text = score_data.name + (" (YOU)" if is_local else "")
|
||||
name_label.add_theme_font_size_override("font_size", 28)
|
||||
name_label.add_theme_color_override("font_color", rank_colors[i])
|
||||
if rank_idx < 3:
|
||||
name_label.add_theme_color_override("font_color", rank_colors[rank_idx])
|
||||
elif is_local:
|
||||
name_label.add_theme_color_override("font_color", Color(0.4, 0.7, 1.0)) # Blue for local player
|
||||
|
||||
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.text = str(score_data.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)
|
||||
return entry
|
||||
|
||||
# 1. Show Top 3
|
||||
for i in range(min(player_scores.size(), 3)):
|
||||
leaderboard_container.add_child(create_entry.call(i))
|
||||
|
||||
# 2. Show Local Player if NOT in Top 3
|
||||
if local_player_rank >= 3:
|
||||
# Add a separator
|
||||
var separator = ColorRect.new()
|
||||
separator.custom_minimum_size = Vector2(0, 2)
|
||||
separator.color = Color(1, 1, 1, 0.2)
|
||||
leaderboard_container.add_child(separator)
|
||||
|
||||
# Add local player entry
|
||||
leaderboard_container.add_child(create_entry.call(local_player_rank))
|
||||
|
||||
# Back to Menu button
|
||||
var back_btn = Button.new()
|
||||
@@ -1901,24 +1945,16 @@ func _on_back_to_menu_pressed():
|
||||
# Properly disconnect from Nakama match
|
||||
_cleanup_multiplayer()
|
||||
|
||||
# Small delay to let cleanup settle
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
|
||||
# Go back to lobby
|
||||
if get_tree():
|
||||
get_tree().change_scene_to_file("res://scenes/lobby.tscn")
|
||||
|
||||
func _cleanup_multiplayer():
|
||||
"""Properly leave Nakama match and cleanup multiplayer state."""
|
||||
print("[Main] Cleaning up multiplayer connection...")
|
||||
|
||||
# Leave the Nakama match through the bridge
|
||||
if NakamaManager.bridge:
|
||||
NakamaManager.bridge.leave()
|
||||
|
||||
# Clear the current match ID
|
||||
NakamaManager.current_match_id = ""
|
||||
|
||||
# Reset multiplayer peer to disconnect cleanly
|
||||
if multiplayer.get_multiplayer_peer():
|
||||
multiplayer.set_multiplayer_peer(null)
|
||||
NakamaManager.cleanup()
|
||||
|
||||
func _deferred_init_leaderboard():
|
||||
"""Initialize leaderboard after a delay to ensure all players are loaded."""
|
||||
|
||||
Reference in New Issue
Block a user