147 lines
4.5 KiB
GDScript
147 lines
4.5 KiB
GDScript
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
|