extends Node class_name ObstacleManager # ObstacleManager - Handles static spawning of walls/blocks on the arena const TILE_OBSTACLE = 4 # Wall const TILE_GROUND = 0 # Standard Floor var main: Node var gridmap: Node func initialize(p_main: Node, p_gridmap: Node): main = p_main gridmap = p_gridmap print("[ObstacleManager] Initialized") func spawn_random_obstacles(count: int = 15): if not multiplayer.is_server(): return print("[ObstacleManager] Attempting to spawn %d obstacles (Guaranteed types first)" % count) var shapes = _get_all_shapes() var successful_spawns = 0 # 1. First, try to spawn each shape type at least once for shape_idx in range(shapes.size()): var shape_attempts = 0 var shape_placed = false while not shape_placed and shape_attempts < 20: # Give each type a fair chance shape_attempts += 1 if spawn_random_wall(shape_idx): shape_placed = true successful_spawns += 1 if successful_spawns >= count: break # 2. Then, fill the remaining count with random shapes var total_attempts = 0 var max_attempts = count * 5 while successful_spawns < count and total_attempts < max_attempts: total_attempts += 1 if spawn_random_wall(): successful_spawns += 1 print("[ObstacleManager] Final: Spawned %d obstacles" % successful_spawns) # Force AStar update after all spawns if gridmap and gridmap.has_method("initialize_astar"): gridmap.initialize_astar() func _get_all_shapes() -> Array: return [ # L (3 blocks) - 4 Rotations [Vector2i(0, 0), Vector2i(1, 0), Vector2i(0, 1)], # L - Corner Bottom Left [Vector2i(0, 0), Vector2i(-1, 0), Vector2i(0, 1)], # L - Corner Bottom Right [Vector2i(0, 0), Vector2i(1, 0), Vector2i(0, -1)], # L - Corner Top Left [Vector2i(0, 0), Vector2i(-1, 0), Vector2i(0, -1)], # L - Corner Top Right # 2 Vertical [Vector2i(0, 0), Vector2i(0, 1)], [Vector2i(0, 0), Vector2i(0, -1)], # 2 Horizontal [Vector2i(0, 0), Vector2i(1, 0)], [Vector2i(0, 0), Vector2i(-1, 0)], # Single [Vector2i(0, 0)] ] func spawn_random_wall(forced_shape_idx: int = -1) -> bool: if not gridmap or not main: return false var cols = gridmap.get("columns") if "columns" in gridmap else 14 var rows = gridmap.get("rows") if "rows" in gridmap else 14 # User Request: "spawn on columns 3 and so on" if cols <= 3: return false var x = randi_range(3, cols - 1) var z = randi_range(0, rows - 1) var shapes = _get_all_shapes() var shape_idx = forced_shape_idx if forced_shape_idx != -1 else randi() % shapes.size() var shape = shapes[shape_idx] var wall_positions: Array[Vector3i] = [] # Validate position and check for ground for offset in shape: var target_x = x + offset.x var target_z = z + offset.y # Bounds check if target_x >= 3 and target_x < cols and target_z >= 0 and target_z < rows: var current_item = gridmap.get_cell_item(Vector3i(target_x, 0, target_z)) # Only spawn on ground (0) to avoid replacing other obstacles or special tiles if current_item == TILE_GROUND: # PLAYER CHECK: Ensure no player is on this tile var is_occupied = false for player in get_tree().get_nodes_in_group("Players"): if player.get("current_position") == Vector2i(target_x, target_z): is_occupied = true break if is_occupied: return false # ADJACENCY CHECK: Ensure no existing walls are nearby if not _is_position_isolated(target_x, target_z, shape, x, z): return false wall_positions.append(Vector3i(target_x, 0, target_z)) else: # If any block of the shape hits a non-ground tile, fail the whole shape for clean placement return false else: return false # Only proceed if we can place the full shape if wall_positions.is_empty(): return false # Create walls on all clients (Permanent, no despawn) for pos in wall_positions: main.rpc("sync_grid_item", pos.x, pos.y, pos.z, TILE_OBSTACLE) return true func _is_position_isolated(target_x: int, target_z: int, shape_offsets: Array, origin_x: int, origin_z: int) -> bool: # Check 3x3 area for dx in range(-1, 2): for dz in range(-1, 2): if dx == 0 and dz == 0: continue var nx = target_x + dx var nz = target_z + dz # Check if this neighbor is part of the shape we are currently placing var is_part_of_shape = false for offset in shape_offsets: if nx == origin_x + offset.x and nz == origin_z + offset.y: is_part_of_shape = true break if is_part_of_shape: continue # Check if gridmap has a wall at this neighbor if gridmap.get_cell_item(Vector3i(nx, 0, nz)) == TILE_OBSTACLE: return false return true