diff --git a/scenes/main.gd b/scenes/main.gd index 9200620..ffd3d31 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -759,6 +759,10 @@ func _start_game(): if LobbyManager.game_mode == "Tekton Doors" and portal_mode_manager: portal_mode_manager._randomize_connections() + # STOP N GO: Rotate players to face East BEFORE countdown + if LobbyManager.game_mode == "Stop n Go" and stop_n_go_manager: + stop_n_go_manager.rotate_players_to_start() + await _start_pre_game_countdown() GameStateManager.start_game() @@ -1507,7 +1511,7 @@ func _deferred_set_player_goals(player_id: int, goals: Array): ui_manager.update_playerboard_ui() -@rpc("any_peer", "call_local") +@rpc("any_peer", "call_local", "reliable") func sync_playerboard(player_id: int, new_playerboard: Array): # Find the player and update their playerboard var player = get_node_or_null(str(player_id)) diff --git a/scenes/player.gd b/scenes/player.gd index 27677ee..f39c1cb 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -935,12 +935,22 @@ func on_stop_phase_violation(): # New Indefinite Freeze until phase ends rpc("sync_stop_freeze", true) - # Scatter items - var items_to_scatter = [] + # Scatter only 3 random items (User request) + var occupied_indices = [] for i in range(playerboard.size()): if playerboard[i] != -1: - items_to_scatter.append(playerboard[i]) - playerboard[i] = -1 + occupied_indices.append(i) + + if occupied_indices.is_empty(): return + + occupied_indices.shuffle() + var scatter_count = min(occupied_indices.size(), 3) + var items_to_scatter = [] + + for i in range(scatter_count): + var idx = occupied_indices[i] + items_to_scatter.append(playerboard[idx]) + playerboard[idx] = -1 rpc("sync_playerboard", playerboard) @@ -2006,7 +2016,10 @@ func _highlight_adjacent_playerboard_slots(): @rpc("any_peer", "call_local", "reliable") func sync_rotation(new_rotation: float): - if not is_multiplayer_authority(): + # Apply rotation if we are NOT the authority (syncing others) + # OR if the server (ID 1) is forcing a rotation (start of match) + var sender_id = multiplayer.get_remote_sender_id() + if not is_multiplayer_authority() or sender_id == 1: rotation.y = new_rotation if movement_manager: movement_manager.target_rotation = new_rotation diff --git a/scripts/managers/stop_n_go_manager.gd b/scripts/managers/stop_n_go_manager.gd index 71a5784..6a4a8c3 100644 --- a/scripts/managers/stop_n_go_manager.gd +++ b/scripts/managers/stop_n_go_manager.gd @@ -213,6 +213,15 @@ func start_game_mode(): # activate_client_side() can be called when first sync arrives pass +func rotate_players_to_start(): + """Force all players to face East (towards finish line). Called before countdown.""" + if not multiplayer.is_server(): return + + var all_players = get_tree().get_nodes_in_group("Players") + for p in all_players: + if p.has_method("rpc"): + p.rpc("sync_rotation", PI/2) + func _start_phase(phase: Phase): current_phase = phase phase_timer = float(LobbyManager.sng_go_duration) if phase == Phase.GO else float(LobbyManager.sng_stop_duration) @@ -226,7 +235,14 @@ func _start_phase(phase: Phase): # --- STATIC SAFE ZONE: Penalize players outside the zone --- var all_players = get_tree().get_nodes_in_group("Players") for p in all_players: - if not _is_in_safe_zone(p.current_position): + # POSITION SYNC FIX: If player is currently moving on their client, + # the server's 'current_position' might still be the old one. + # Use 'target_position' if they are moving. + var check_pos = p.current_position + if p.get("is_player_moving") and p.get("target_position") != Vector2i(-1, -1): + check_pos = p.target_position + + if not _is_in_safe_zone(check_pos): _scatter_player_tiles(p) # Refresh power-ups every STOP phase @@ -550,7 +566,12 @@ func _is_in_safe_zone(pos: Vector2i) -> bool: gridmap = get_node_or_null("/root/Main/EnhancedGridMap") if not gridmap: return false - # Check Layer 2 (Overlay layer) for Safe Zone tile + # 1. Check Floor 0 for Start (3) or Finish (3) lines + var floor_tile = gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y)) + if floor_tile == TILE_START or floor_tile == TILE_FINISH: + return true + + # 2. Check Layer 2 (Overlay layer) for Dynamic Safe Zone tile var overlay_tile = gridmap.get_cell_item(Vector3i(pos.x, 2, pos.y)) return overlay_tile == TILE_SAFE @@ -715,7 +736,11 @@ func _scatter_player_tiles(player_node: Node): main.rpc("sync_grid_item", drop_pos.x, 1, drop_pos.y, tile) # Sync cleared playerboard to all clients - main.rpc("sync_playerboard", peer_id, playerboard) + # Sync cleared playerboard via the player's own RPC (more robust than Main lookup) + player_node.rpc("sync_playerboard", playerboard) + + # FREEZE FIX: If they were caught outside, they should also be frozen like a movement violation + player_node.rpc("sync_stop_freeze", true) # Notify the player SfxManager.rpc("play_rpc", "tile_scatter") @@ -725,7 +750,7 @@ func _scatter_player_tiles(player_node: Node): if player_node.has_method("trigger_screen_shake") and can_rpc(): player_node.rpc("trigger_screen_shake", "heavy") - print("[StopNGo] Scattered %d tiles from Player %d" % [tiles_to_scatter.size(), peer_id]) + print("[StopNGo] Caught outside: Scattered %d tiles from Player %d" % [tiles_to_scatter.size(), peer_id]) # ============================================================================= # OLD STATIC SAFE ZONE LOGIC (Retained for Reference)