feat: Implement core game logic, player script, and the Stop n Go game mode with its dedicated manager.
This commit is contained in:
+5
-1
@@ -759,6 +759,10 @@ func _start_game():
|
|||||||
if LobbyManager.game_mode == "Tekton Doors" and portal_mode_manager:
|
if LobbyManager.game_mode == "Tekton Doors" and portal_mode_manager:
|
||||||
portal_mode_manager._randomize_connections()
|
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()
|
await _start_pre_game_countdown()
|
||||||
|
|
||||||
GameStateManager.start_game()
|
GameStateManager.start_game()
|
||||||
@@ -1507,7 +1511,7 @@ func _deferred_set_player_goals(player_id: int, goals: Array):
|
|||||||
ui_manager.update_playerboard_ui()
|
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):
|
func sync_playerboard(player_id: int, new_playerboard: Array):
|
||||||
# Find the player and update their playerboard
|
# Find the player and update their playerboard
|
||||||
var player = get_node_or_null(str(player_id))
|
var player = get_node_or_null(str(player_id))
|
||||||
|
|||||||
+18
-5
@@ -935,12 +935,22 @@ func on_stop_phase_violation():
|
|||||||
# New Indefinite Freeze until phase ends
|
# New Indefinite Freeze until phase ends
|
||||||
rpc("sync_stop_freeze", true)
|
rpc("sync_stop_freeze", true)
|
||||||
|
|
||||||
# Scatter items
|
# Scatter only 3 random items (User request)
|
||||||
var items_to_scatter = []
|
var occupied_indices = []
|
||||||
for i in range(playerboard.size()):
|
for i in range(playerboard.size()):
|
||||||
if playerboard[i] != -1:
|
if playerboard[i] != -1:
|
||||||
items_to_scatter.append(playerboard[i])
|
occupied_indices.append(i)
|
||||||
playerboard[i] = -1
|
|
||||||
|
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)
|
rpc("sync_playerboard", playerboard)
|
||||||
|
|
||||||
@@ -2006,7 +2016,10 @@ func _highlight_adjacent_playerboard_slots():
|
|||||||
|
|
||||||
@rpc("any_peer", "call_local", "reliable")
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
func sync_rotation(new_rotation: float):
|
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
|
rotation.y = new_rotation
|
||||||
if movement_manager:
|
if movement_manager:
|
||||||
movement_manager.target_rotation = new_rotation
|
movement_manager.target_rotation = new_rotation
|
||||||
|
|||||||
@@ -213,6 +213,15 @@ func start_game_mode():
|
|||||||
# activate_client_side() can be called when first sync arrives
|
# activate_client_side() can be called when first sync arrives
|
||||||
pass
|
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):
|
func _start_phase(phase: Phase):
|
||||||
current_phase = phase
|
current_phase = phase
|
||||||
phase_timer = float(LobbyManager.sng_go_duration) if phase == Phase.GO else float(LobbyManager.sng_stop_duration)
|
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 ---
|
# --- STATIC SAFE ZONE: Penalize players outside the zone ---
|
||||||
var all_players = get_tree().get_nodes_in_group("Players")
|
var all_players = get_tree().get_nodes_in_group("Players")
|
||||||
for p in all_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)
|
_scatter_player_tiles(p)
|
||||||
|
|
||||||
# Refresh power-ups every STOP phase
|
# 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")
|
gridmap = get_node_or_null("/root/Main/EnhancedGridMap")
|
||||||
if not gridmap: return false
|
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))
|
var overlay_tile = gridmap.get_cell_item(Vector3i(pos.x, 2, pos.y))
|
||||||
return overlay_tile == TILE_SAFE
|
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)
|
main.rpc("sync_grid_item", drop_pos.x, 1, drop_pos.y, tile)
|
||||||
|
|
||||||
# Sync cleared playerboard to all clients
|
# 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
|
# Notify the player
|
||||||
SfxManager.rpc("play_rpc", "tile_scatter")
|
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():
|
if player_node.has_method("trigger_screen_shake") and can_rpc():
|
||||||
player_node.rpc("trigger_screen_shake", "heavy")
|
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)
|
# OLD STATIC SAFE ZONE LOGIC (Retained for Reference)
|
||||||
|
|||||||
Reference in New Issue
Block a user