From 7bc3ba2a99c9f7e854c88495478352143eceb7a5 Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Tue, 17 Mar 2026 18:04:29 +0800 Subject: [PATCH] feat: Add main game scene script with manager initialization, UI setup, multiplayer logic, and game mode specific configurations. --- scenes/main.gd | 103 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 22 deletions(-) diff --git a/scenes/main.gd b/scenes/main.gd index 7048423..5c93202 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -523,11 +523,14 @@ func _setup_host_game(): print("Spawning lobby player: ", peer_id) _spawn_lobby_client_sync(peer_id) - # 1. PVT: Pre-calculate Static Tekton positions so we know where NOT to spawn players + # Spawning and arena setup + if LobbyManager.game_mode == "Stop n Go" and stop_n_go_manager: + stop_n_go_manager._setup_arena() + elif LobbyManager.game_mode == "Tekton Doors" and portal_mode_manager: + portal_mode_manager.setup_arena_locally() + + # 1. PVT: Pre-calculate Static Tekton positions AFTER arena size is known _precalculate_static_positions() - - # Delay spawn assignment until ALL players (including bots) are spawned - # Moved _assign_random_spawn_positions() to after bot loop # Wait for players to be fully ready (player.gd has 0.1s await in _ready before managers init) # Faster for LAN mode @@ -565,14 +568,6 @@ func _setup_host_game(): # Ensure Bots are in the tree before assigning positions await get_tree().process_frame - # INITIALIZE ARENA SIZE for Stop n Go BEFORE spawning players, to prevent out-of-bounds - if LobbyManager.game_mode == "Stop n Go" and stop_n_go_manager: - stop_n_go_manager._setup_arena() - - # Arena is set up, wait for __start_game to assign positions where Socket is open - if LobbyManager.game_mode == "Tekton Doors" and portal_mode_manager: - portal_mode_manager.setup_arena_locally() - _start_game() func _spawn_lobby_client_sync(peer_id: int): @@ -813,10 +808,10 @@ func _assign_random_spawn_positions(): var pos = Vector2i(x, z) # SAFETY CHECK: Is this reserved for a Static Tekton Stand? - # Stand covers [center-1, center+1] + # Stand clears 3x3, but we use a radius of 2 (5x5) for safety var is_safe = true for reserved in reserved_static_positions: - if abs(x - reserved.x) <= 1 and abs(z - reserved.y) <= 1: + if abs(x - reserved.x) <= 2 and abs(z - reserved.y) <= 2: is_safe = false break @@ -924,7 +919,7 @@ func _assign_stop_n_go_spawn_positions(all_players: Array): print("[StopNGo] Assigned spawn %s to player %s" % [assigned_pos, player.name]) func _assign_portal_mode_spawn_positions(all_players: Array): - """Assigns spawns to different quadrants for Tekton Doors mode.""" + """Assigns spawns to different quadrants for Tekton Doors mode, avoiding stands and intersections.""" if not portal_mode_manager: _assign_random_spawn_positions() # Fallback return @@ -932,13 +927,57 @@ func _assign_portal_mode_spawn_positions(all_players: Array): # Sort players for deterministic assignment all_players.sort_custom(func(a, b): return a.name.to_int() < b.name.to_int()) - var spawn_points = portal_mode_manager.get_spawn_points() + # Get baseline quadrant centers (3,3), (10,3), etc. + var base_spawn_points = portal_mode_manager.get_spawn_points() var spawn_index = 0 + var assigned_positions: Array[Vector2i] = [] for player in all_players: - var assigned_pos = spawn_points[spawn_index % spawn_points.size()] + var center_pos = base_spawn_points[spawn_index % base_spawn_points.size()] + var assigned_pos = center_pos # Fallback position - # Sync + # Spiral search for a valid spot (walkable, not in stand zone, not occupied) + var found = false + for radius in range(0, 5): # Increase search radius + for dx in range(-radius, radius + 1): + for dz in range(-radius, radius + 1): + # Only check the "ring" at the current radius + if abs(dx) != radius and abs(dz) != radius and radius > 0: + continue + + var test_pos = center_pos + Vector2i(dx, dz) + + # 1. Check map bounds + var em = $EnhancedGridMap + if not em or test_pos.x < 0 or test_pos.x >= em.columns or test_pos.y < 0 or test_pos.y >= em.rows: + continue + + # 2. Check if walkable floor (Floor 0, ID 0) + if em.get_cell_item(Vector3i(test_pos.x, 0, test_pos.y)) != 0: + continue + + # 3. Check if reserved for a Static Tekton Stand (3x3 area, use 2-tile buffer) + var is_reserved = false + for reserved in reserved_static_positions: + if abs(test_pos.x - reserved.x) <= 2 and abs(test_pos.y - reserved.y) <= 2: + is_reserved = true + break + if is_reserved: + continue + + # 4. Check if occupied by another already-assigned player + if assigned_positions.has(test_pos): + continue + + assigned_pos = test_pos + found = true + break + if found: break + if found: break + + assigned_positions.append(assigned_pos) + + # Sync and place player.position = player.grid_to_world(assigned_pos) player.current_position = assigned_pos player.is_player_moving = false @@ -948,7 +987,7 @@ func _assign_portal_mode_spawn_positions(all_players: Array): player.rpc("set_spawn_position", assigned_pos) spawn_index += 1 - print("[PortalMode] Assigned Room Quadrant %s to player %s" % [assigned_pos, player.name]) + print("[PortalMode] Assigned Quadrant Pos %s to player %s" % [assigned_pos, player.name]) # ============================================================================= # Tekton NPC Management @@ -1072,12 +1111,25 @@ func spawn_static_tektons(): if not static_tekton_manager: static_tekton_manager = StaticTektonManager.new() - # Use pre-calculated points if available, otherwise calculate new ones - var spawn_points = [] + # Use pre-calculated points if available + var spawn_points: Array[Vector2i] = [] if not reserved_static_positions.is_empty(): spawn_points = reserved_static_positions else: - spawn_points = static_tekton_manager.calculate_spawn_points(3, enhanced_gridmap) + # If pre-calculation failed, we MUST check for player overlaps now + var raw_points = static_tekton_manager.calculate_spawn_points(3, enhanced_gridmap) + var all_players = get_tree().get_nodes_in_group("Players") + + for p_spot in raw_points: + var is_overlapping = false + for player in all_players: + if abs(p_spot.x - player.current_position.x) <= 2 and abs(p_spot.y - player.current_position.y) <= 2: + is_overlapping = true + break + if not is_overlapping: + spawn_points.append(p_spot) + + reserved_static_positions = spawn_points # Save them print("[Main] Spawning Static Tektons at: %s" % str(spawn_points)) @@ -1682,6 +1734,13 @@ func randomize_game_grid(): var enhanced_gridmap = $EnhancedGridMap if enhanced_gridmap: + # FIRST: Ensure Floor 0 is entirely filled with walkable ground (ID 0) + # This ensures StaticTektonManager calculation (which checks Floor 0) succeeds. + for x in range(enhanced_gridmap.columns): + for z in range(enhanced_gridmap.rows): + if enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) == -1: + enhanced_gridmap.set_cell_item(Vector3i(x, 0, z), 0) + # Custom spawn ratio for Free Mode: 80% common tiles, 20% empty tiles (start of game) var density_callable = func(): if randf() <= 0.8: