feat: Implement a new lobby system with configurable match duration, game over screen, and core game state management.

This commit is contained in:
2025-12-20 01:10:49 +08:00
parent 75eb398649
commit b0d45d4569
12 changed files with 1241 additions and 338 deletions
+118 -2
View File
@@ -11,6 +11,10 @@ signal player_left(player_id: int)
signal ready_state_changed(player_id: int, is_ready: bool)
signal all_players_ready()
signal game_starting()
signal match_duration_changed(duration_seconds: int)
signal character_changed(player_id: int, character_name: String)
signal area_changed(area_name: String)
signal player_list_changed()
# Room data structure
var current_room: Dictionary = {}
@@ -19,6 +23,15 @@ var available_rooms: Array = []
var is_host: bool = false
var local_player_name: String = "Player"
# Match duration in seconds (configurable in lobby by host)
var match_duration: int = 180 # Default 3 minutes
# Character and area selection
var available_characters: Array[String] = ["Bob", "Gatot", "Masbro", "Oldpop"]
var available_areas: Array[String] = ["Desert", "Forest", "City", "Factory"]
var selected_area: String = "Desert" # Host-controlled
var local_character_index: int = 0 # Local player's character index
# Ready to start game check
var _all_ready: bool = false
@@ -133,6 +146,101 @@ func is_all_ready() -> bool:
# Game Start
# =============================================================================
func set_match_duration(duration_seconds: int) -> void:
"""Host sets match duration. Syncs to all clients."""
match_duration = duration_seconds
if is_host:
rpc("sync_match_duration", duration_seconds)
@rpc("authority", "call_local", "reliable")
func sync_match_duration(duration_seconds: int) -> void:
"""Sync match duration from host to clients."""
match_duration = duration_seconds
emit_signal("match_duration_changed", duration_seconds)
func get_match_duration() -> int:
return match_duration
# =============================================================================
# Character Selection
# =============================================================================
func get_local_character() -> String:
"""Get the local player's current character name."""
return available_characters[local_character_index]
func set_character(character_name: String) -> void:
"""Set local player's character. Syncs to all peers."""
var idx = available_characters.find(character_name)
if idx == -1:
push_error("Invalid character: " + character_name)
return
local_character_index = idx
var my_id = multiplayer.get_unique_id()
# Update local player data
for player in players_in_room:
if player["id"] == my_id:
player["character"] = character_name
break
# Sync to all peers
rpc("sync_character", my_id, character_name)
func cycle_character(direction: int) -> void:
"""Cycle through available characters. direction: -1 for left, +1 for right."""
local_character_index = wrapi(local_character_index + direction, 0, available_characters.size())
set_character(available_characters[local_character_index])
@rpc("any_peer", "call_local", "reliable")
func sync_character(player_id: int, character_name: String) -> void:
"""Sync character selection across all clients."""
for player in players_in_room:
if player["id"] == player_id:
player["character"] = character_name
break
emit_signal("character_changed", player_id, character_name)
emit_signal("player_list_changed")
# =============================================================================
# Area Selection (Host Only)
# =============================================================================
func get_selected_area() -> String:
return selected_area
func get_area_index() -> int:
return available_areas.find(selected_area)
func set_area(area_name: String) -> void:
"""Host sets the game area. Syncs to all clients."""
if not is_host:
push_warning("Only host can change area")
return
if area_name not in available_areas:
push_error("Invalid area: " + area_name)
return
selected_area = area_name
rpc("sync_area", area_name)
func cycle_area(direction: int) -> void:
"""Host cycles through available areas. direction: -1 for left, +1 for right."""
if not is_host:
return
var current_idx = available_areas.find(selected_area)
var new_idx = wrapi(current_idx + direction, 0, available_areas.size())
set_area(available_areas[new_idx])
@rpc("authority", "call_local", "reliable")
func sync_area(area_name: String) -> void:
"""Sync area selection from host to clients."""
selected_area = area_name
emit_signal("area_changed", area_name)
func start_game() -> void:
"""Host triggers game start (transitions all players to main.tscn)."""
if not is_host:
@@ -143,6 +251,9 @@ func start_game() -> void:
push_error("Not all players are ready")
return
# Sync match duration to all clients before starting
rpc("sync_match_duration", match_duration)
# Notify all clients to start
rpc("_on_game_starting")
@@ -168,7 +279,8 @@ func _on_match_joined(match_id: String) -> void:
var my_data = {
"id": my_id,
"name": local_player_name,
"is_ready": false
"is_ready": false,
"character": available_characters[local_character_index]
}
players_in_room.append(my_data)
@@ -202,7 +314,8 @@ func _on_peer_connected(peer_id: int) -> void:
var new_player = {
"id": peer_id,
"name": "Player %d" % peer_id,
"is_ready": false
"is_ready": false,
"character": available_characters[0]
}
players_in_room.append(new_player)
@@ -247,3 +360,6 @@ func reset() -> void:
available_rooms.clear()
is_host = false
_all_ready = false
match_duration = 180 # Reset to default 3 minutes
selected_area = "Desert"
local_character_index = 0