extends RefCounted

# Dungeon Generator for RPG DUNGEON VOL 3 tilemap
# Tiles are 16x16 pixels
# Walls are 32x32 pixels (2x2 tiles)
# Ground is 1 tile (16x16)

# Tile coordinates for RPG DUNGEON VOL 3
# Coordinates are (x, y) where x is column, y is row in the tileset
# Tiles are 16x16 pixels each

# Room wall tiles (outer walls)
# Walls are 2 tiles tall (upper and lower parts) or 2 tiles wide (left and right parts)
# Corner tiles are 2x2 (4 tiles total)

# Top-left corner (2x2): (0,0), (1,0), (0,1), (1,1)
const WALL_TOP_LEFT_TOP_LEFT = Vector2i(5, 0) # Top-left corner, top-left part
const WALL_TOP_LEFT_TOP_RIGHT = Vector2i(1, 0) # Top-left corner, top-right part
const WALL_TOP_LEFT_BOTTOM_LEFT = Vector2i(0, 1) # Top-left corner, bottom-left part
const WALL_TOP_LEFT_BOTTOM_RIGHT = Vector2i(1, 1) # Top-left corner, bottom-right part

# Top-right corner (2x2): (3,0), (4,0), (3,1), (4,1)
const WALL_TOP_RIGHT_TOP_LEFT = Vector2i(3, 0) # Top-right corner, top-left part
const WALL_TOP_RIGHT_TOP_RIGHT = Vector2i(4, 0) # Top-right corner, top-right part
const WALL_TOP_RIGHT_BOTTOM_LEFT = Vector2i(3, 1) # Top-right corner, bottom-left part
const WALL_TOP_RIGHT_BOTTOM_RIGHT = Vector2i(4, 1) # Top-right corner, bottom-right part

# Top wall (1x2 tile - spans 2 tiles tall)
const WALL_TOP_UPPER = Vector2i(2, 0) # Top wall, upper part
const WALL_TOP_LOWER = Vector2i(2, 1) # Top wall, lower part

# Left wall (2 tiles wide - spans 2 tiles horizontally)
const WALL_LEFT_LEFT = Vector2i(0, 2) # Left wall, left part
const WALL_LEFT_RIGHT = Vector2i(1, 2) # Left wall, right part

# Right wall (2 tiles wide - spans 2 tiles horizontally)
const WALL_RIGHT_LEFT = Vector2i(3, 2) # Right wall, left part
const WALL_RIGHT_RIGHT = Vector2i(4, 2) # Right wall, right part

# Bottom-left corner (2x2): (0,3), (1,3), (0,4), (1,4)
const WALL_BOTTOM_LEFT_TOP_LEFT = Vector2i(0, 3) # Bottom-left corner, top-left part
const WALL_BOTTOM_LEFT_TOP_RIGHT = Vector2i(1, 3) # Bottom-left corner, top-right part
const WALL_BOTTOM_LEFT_BOTTOM_LEFT = Vector2i(0, 4) # Bottom-left corner, bottom-left part
const WALL_BOTTOM_LEFT_BOTTOM_RIGHT = Vector2i(1, 4) # Bottom-left corner, bottom-right part

# Bottom-right corner (2x2): (3,3), (4,3), (3,4), (4,4)
const WALL_BOTTOM_RIGHT_TOP_LEFT = Vector2i(3, 3) # Bottom-right corner, top-left part
const WALL_BOTTOM_RIGHT_TOP_RIGHT = Vector2i(4, 3) # Bottom-right corner, top-right part
const WALL_BOTTOM_RIGHT_BOTTOM_LEFT = Vector2i(3, 4) # Bottom-right corner, bottom-left part
const WALL_BOTTOM_RIGHT_BOTTOM_RIGHT = Vector2i(4, 4) # Bottom-right corner, bottom-right part

# Bottom wall (1x2 tile - spans 2 tiles tall)
const WALL_BOTTOM_UPPER = Vector2i(2, 3) # Bottom wall, upper part
const WALL_BOTTOM_LOWER = Vector2i(2, 4) # Bottom wall, lower part

# Inner wall tiles (for non-rectangular rooms)
const INNER_WALL_TOP_LEFT = Vector2i(1, 6)
const INNER_WALL_TOP_RIGHT = Vector2i(3, 6)
const INNER_WALL_BOTTOM_LEFT = Vector2i(1, 8)
const INNER_WALL_BOTTOM_RIGHT = Vector2i(3, 8)

# Door tiles
const DOOR_UP_START = Vector2i(7, 0) # 3x2 large
const DOOR_LEFT_START = Vector2i(5, 2) # 2x3 large
const DOOR_RIGHT_START = Vector2i(10, 2) # 2x3 large
const DOOR_BOTTOM_START = Vector2i(7, 5) # 3x2 large

# Stairs tiles (exit room) - similar to doors but different middle frame tile
const STAIRS_UP_START = Vector2i(7, 0) # 3x2 large, middle tile is (10,0) instead of (8,0)
const STAIRS_LEFT_START = Vector2i(5, 2) # 2x3 large, middle tile is (5,1) instead of (5,3)
const STAIRS_RIGHT_START = Vector2i(10, 2) # 2x3 large, middle tile is (11,1) instead of (11,3)
const STAIRS_DOWN_START = Vector2i(7, 5) # 3x2 large, middle tile is (6,6) instead of (8,6)

# Ground/floor tiles (random selection)
const FLOOR_TILES = [Vector2i(9, 8), Vector2i(14, 8), Vector2i(6, 11)]

# Room generation parameters
# Minimum room size is 3x3 floor tiles, but rooms need 2-tile walls on each side
# So minimum room size including walls is 3 + 2*2 = 7 tiles (3 floor + 2 walls on each side)
# But we want at least 3x3 floor, so room size should be at least 7x7 total
const MIN_ROOM_SIZE = 7 # Minimum room size in tiles (includes walls, so 3x3 floor minimum)
const MAX_ROOM_SIZE = 12 # Maximum room size in tiles
const MIN_HOLE_SIZE = 9 # Minimum hole size in rooms (9x9 tiles)
const DOOR_MIN_WIDTH = 3 # Minimum width for door frames
const DOOR_MAX_WIDTH = 5 # Maximum width for door frames
const CORRIDOR_WIDTH = 1 # Corridor width in tiles (1 tile)

# Main generation function
func generate_dungeon(map_size: Vector2i, seed_value: int = 0, level: int = 1) -> Dictionary:
	var rng = RandomNumberGenerator.new()
	if seed_value > 0:
		rng.seed = seed_value
	else:
		rng.randomize()
	
	# Calculate target room count based on level
	# Level 1: 7-8 rooms, then increase by 2-3 rooms per level
	var target_room_count = 7 + (level - 1) * 2 + rng.randi_range(0, 1) # Level 1: 7-8, Level 2: 9-10, etc.
	LogManager.log("DungeonGenerator: Level " + str(level) + " - Target room count: " + str(target_room_count), LogManager.CATEGORY_DUNGEON)
	
	# Initialize grid (0 = wall, 1 = floor, 2 = door, 3 = corridor)
	var grid = []
	var tile_grid = [] # Actual tile coordinates (Vector2i) for rendering
	for x in range(map_size.x):
		grid.append([])
		tile_grid.append([])
		for y in range(map_size.y):
			grid[x].append(0) # Start with all walls
			tile_grid[x].append(Vector2i(0, 0)) # Default wall tile (will be set properly later)
	
	var all_rooms = []
	var all_doors = []
	
	# 1. Create first room at a random position
	var first_w = rng.randi_range(MIN_ROOM_SIZE, MAX_ROOM_SIZE)
	var first_h = rng.randi_range(MIN_ROOM_SIZE, MAX_ROOM_SIZE)
	var first_room = {
		"x": rng.randi_range(4, map_size.x - first_w - 4),
		"y": rng.randi_range(4, map_size.y - first_h - 4),
		"w": first_w,
		"h": first_h,
		"modifiers": []
	}
	
	_set_floor(first_room, grid, tile_grid, map_size, rng)
	all_rooms.append(first_room)
	
	# 2. Try to place rooms until we reach target count or can't fit any more
	var attempts = 1000
	while attempts > 0 and all_rooms.size() < target_room_count and all_rooms.size() > 0:
		var source_room = all_rooms[rng.randi() % all_rooms.size()]
		var new_room = _try_place_room_near(source_room, grid, map_size, rng)
		
		if new_room.w > 0:
			_set_floor(new_room, grid, tile_grid, map_size, rng)
			all_rooms.append(new_room)
		
		attempts -= 1
	
	LogManager.log("DungeonGenerator: Generated " + str(all_rooms.size()) + " rooms (target was " + str(target_room_count) + ")", LogManager.CATEGORY_DUNGEON)
	
	# 3. Connect rooms with corridors/doors
	if all_rooms.size() > 1:
		_connect_rooms(all_rooms, grid, tile_grid, map_size, all_doors, rng)
	
	# 4. Add random holes in some rooms (minimum 9x9 tiles)
	for room in all_rooms:
		if rng.randf() < 0.3: # 30% chance for a hole
			_add_hole_to_room(room, grid, tile_grid, map_size, rng)
	
	# 5. Mark start room (random room for variety)
	var start_room_index = rng.randi() % all_rooms.size()
	var exit_room_index = -1 # Declare exit_room_index early to avoid scope issues
	all_rooms[start_room_index].modifiers.append({"type": "START"})
	
	# 6. Mark exit room (farthest REACHABLE room from start)
	# First find all reachable rooms from start
	var reachable_rooms = _find_reachable_rooms(all_rooms[start_room_index], all_rooms, all_doors)
	LogManager.log("DungeonGenerator: Found " + str(reachable_rooms.size()) + " reachable rooms from start (out of " + str(all_rooms.size()) + " total)", LogManager.CATEGORY_DUNGEON)
	
	# CRITICAL: Remove inaccessible rooms (rooms not reachable from start)
	# Store the start room before filtering (it should always be reachable)
	var start_room_ref = all_rooms[start_room_index]
	var inaccessible_count = 0
	
	# Create new array with only reachable rooms
	# Use value-based comparison (x, y, w, h) to check if room is reachable
	var filtered_rooms = []
	for room in all_rooms:
		var is_reachable = false
		# Check if this room is in the reachable_rooms list by comparing values
		for reachable_room in reachable_rooms:
			if reachable_room.x == room.x and reachable_room.y == room.y and \
			   reachable_room.w == room.w and reachable_room.h == room.h:
				is_reachable = true
				break
		
		if is_reachable:
			filtered_rooms.append(room)
		else:
			inaccessible_count += 1
			LogManager.log("DungeonGenerator: Removing inaccessible room at (" + str(room.x) + ", " + str(room.y) + ") - no corridor connection", LogManager.CATEGORY_DUNGEON)
	
	# Update all_rooms to only include reachable rooms
	all_rooms = filtered_rooms
	
	if inaccessible_count > 0:
		LogManager.log("DungeonGenerator: Removed " + str(inaccessible_count) + " inaccessible room(s). Remaining rooms: " + str(all_rooms.size()), LogManager.CATEGORY_DUNGEON)
	
	# Update start_room_index after filtering (find start room in new array using value-based comparison)
	start_room_index = -1
	for i in range(all_rooms.size()):
		var room = all_rooms[i]
		if room.x == start_room_ref.x and room.y == start_room_ref.y and \
		   room.w == start_room_ref.w and room.h == start_room_ref.h:
			start_room_index = i
			break
	
	if start_room_index == -1:
		LogManager.log_error("DungeonGenerator: ERROR - Start room was removed! This should never happen!", LogManager.CATEGORY_DUNGEON)
		start_room_index = 0 # Fallback
	
	# Also remove doors connected to inaccessible rooms (clean up all_doors)
	var filtered_doors = []
	var doors_removed = 0
	for door in all_doors:
		var door_room1_reachable = false
		var door_room2_reachable = false
		
		# Check if door's connected rooms are in the filtered reachable rooms list (all_rooms now only contains reachable rooms)
		# Compare rooms by properties (x, y, w, h) since dictionary comparison might not work
		# Check both room1 and room2 against all reachable rooms
		if "room1" in door and door.room1 is Dictionary and not door.room1.is_empty():
			for room in all_rooms:
				if door.room1.x == room.x and door.room1.y == room.y and \
				   door.room1.w == room.w and door.room1.h == room.h:
					door_room1_reachable = true
					break # Found room1, no need to keep checking
		
		if "room2" in door and door.room2 is Dictionary and not door.room2.is_empty():
			for room in all_rooms:
				if door.room2.x == room.x and door.room2.y == room.y and \
				   door.room2.w == room.w and door.room2.h == room.h:
					door_room2_reachable = true
					break # Found room2, no need to keep checking
		
		# Only keep doors that connect two reachable rooms
		if door_room1_reachable and door_room2_reachable:
			filtered_doors.append(door)
		else:
			doors_removed += 1
			LogManager.log("DungeonGenerator: Removing door - room1 reachable: " + str(door_room1_reachable) + ", room2 reachable: " + str(door_room2_reachable), LogManager.CATEGORY_DUNGEON)
	
	all_doors = filtered_doors
	if doors_removed > 0:
		LogManager.log("DungeonGenerator: Removed " + str(doors_removed) + " door(s) connected to inaccessible rooms. Remaining doors: " + str(all_doors.size()), LogManager.CATEGORY_DUNGEON)
	
	# Find the farthest reachable room (now all rooms are reachable, but find farthest)
	# Make sure we have at least 2 rooms (start and exit must be different)
	# exit_room_index is already declared at function level
	if all_rooms.size() < 2:
		LogManager.log_error("DungeonGenerator: ERROR - Not enough reachable rooms! Need at least 2 (start + exit), but only have " + str(all_rooms.size()), LogManager.CATEGORY_DUNGEON)
		# Use start room as exit if only one room exists (shouldn't happen, but handle gracefully)
		if all_rooms.size() == 1:
			exit_room_index = 0
		else:
			# No rooms at all - this is a critical error
			LogManager.log_error("DungeonGenerator: CRITICAL ERROR - No rooms left after filtering!", LogManager.CATEGORY_DUNGEON)
			return {} # Return empty dungeon
	else:
		exit_room_index = _find_farthest_room(all_rooms, start_room_index)
	
	# Make sure exit room is different from start room
	if exit_room_index == start_room_index and all_rooms.size() > 1:
		# If exit is same as start, find second farthest
		var max_distance = 0
		var second_farthest = -1
		for i in range(all_rooms.size()):
			if i == start_room_index:
				continue
			var distance = abs(all_rooms[i].x - all_rooms[start_room_index].x) + abs(all_rooms[i].y - all_rooms[start_room_index].y)
			if distance > max_distance:
				max_distance = distance
				second_farthest = i
		if second_farthest != -1:
			exit_room_index = second_farthest
	
	all_rooms[exit_room_index].modifiers.append({"type": "EXIT"})
	LogManager.log("DungeonGenerator: Selected exit room at index " + str(exit_room_index) + " position: " + str(all_rooms[exit_room_index].x) + "," + str(all_rooms[exit_room_index].y), LogManager.CATEGORY_DUNGEON)
	
	# 7. Render walls around rooms
	_render_room_walls(all_rooms, grid, tile_grid, map_size, rng)
	
	# 7.5. Place stairs in exit room BEFORE placing torches (so torches don't overlap stairs)
	var stairs_data = _place_stairs_in_exit_room(all_rooms[exit_room_index], grid, tile_grid, map_size, all_doors, rng)
	if stairs_data.is_empty():
		LogManager.log_error("DungeonGenerator: ERROR - Failed to place stairs in exit room! Room size: " + str(all_rooms[exit_room_index].w) + "x" + str(all_rooms[exit_room_index].h) + " Doors: " + str(all_doors.size()), LogManager.CATEGORY_DUNGEON)
		# CRITICAL: Force place stairs - we MUST have an exit!
		LogManager.log("DungeonGenerator: FORCING stairs placement in exit room center", LogManager.CATEGORY_DUNGEON)
		stairs_data = _force_place_stairs(all_rooms[exit_room_index], grid, tile_grid, map_size, all_doors, rng)
		if stairs_data.is_empty():
			LogManager.log_error("DungeonGenerator: CRITICAL ERROR - Could not place stairs even with force placement!", LogManager.CATEGORY_DUNGEON)
	
	# 7.6. Place entrance in start room (only for level > 1) BEFORE placing torches
	var entrance_data = {}
	if level > 1:
		entrance_data = _place_entrance_in_start_room(all_rooms[start_room_index], grid, tile_grid, map_size, all_doors, rng)
		if entrance_data.is_empty():
			LogManager.log_error("DungeonGenerator: ERROR - Failed to place entrance in start room! Room size: " + str(all_rooms[start_room_index].w) + "x" + str(all_rooms[start_room_index].h) + " Doors: " + str(all_doors.size()), LogManager.CATEGORY_DUNGEON)
			# CRITICAL: Force place entrance - we MUST have an entrance for level > 1!
			LogManager.log("DungeonGenerator: FORCING entrance placement in start room", LogManager.CATEGORY_DUNGEON)
			entrance_data = _force_place_stairs(all_rooms[start_room_index], grid, tile_grid, map_size, all_doors, rng)
			if entrance_data.is_empty():
				LogManager.log_error("DungeonGenerator: CRITICAL ERROR - Could not place entrance even with force placement!", LogManager.CATEGORY_DUNGEON)
	
	# 8. Place torches in rooms (AFTER stairs/entrance, so torches don't overlap)
	var all_torches = []
	for room in all_rooms:
		var room_torches = _place_torches_in_room(room, grid, all_doors, map_size, rng)
		all_torches.append_array(room_torches)
	
	# 11. Place blocking doors on existing tile doors (after everything else is created)
	# IMPORTANT: This must happen BEFORE placing enemies, so we know which rooms have monster spawner puzzles
	var blocking_doors_result = _place_blocking_doors(all_rooms, all_doors, grid, map_size, rng, start_room_index, exit_room_index)
	var blocking_doors = blocking_doors_result.doors if blocking_doors_result.has("doors") else blocking_doors_result
	var room_puzzle_data = blocking_doors_result.puzzle_data if blocking_doors_result.has("puzzle_data") else {}
	
	# Extract rooms with monster spawner puzzles (these should NOT have pre-spawned enemies)
	var rooms_with_spawner_puzzles = []
	var blocking_doors_array = blocking_doors if blocking_doors is Array else blocking_doors.doors
	for door_data in blocking_doors_array:
		if "puzzle_type" in door_data and door_data.puzzle_type == "enemy":
			if "blocking_room" in door_data and not door_data.blocking_room.is_empty():
				var puzzle_room = door_data.blocking_room
				# Check if this room is already in the list (avoid duplicates)
				var already_in_list = false
				for existing_room in rooms_with_spawner_puzzles:
					if existing_room.x == puzzle_room.x and existing_room.y == puzzle_room.y and \
					   existing_room.w == puzzle_room.w and existing_room.h == puzzle_room.h:
						already_in_list = true
						break
				if not already_in_list:
					rooms_with_spawner_puzzles.append(puzzle_room)
					LogManager.log("DungeonGenerator: Room (" + str(puzzle_room.x) + ", " + str(puzzle_room.y) + ") has monster spawner puzzle - will skip pre-spawning enemies", LogManager.CATEGORY_DUNGEON)
	
	# 9. Place enemies in rooms (scaled by level, excluding start and exit rooms, and rooms with spawner puzzles)
	var all_enemies = []
	for i in range(all_rooms.size()):
		var room = all_rooms[i]
		# Skip start room and exit room
		if i != start_room_index and i != exit_room_index:
			# CRITICAL: Skip rooms that have monster spawner puzzles (these will spawn enemies when player enters)
			var has_spawner_puzzle = false
			for spawner_room in rooms_with_spawner_puzzles:
				if spawner_room.x == room.x and spawner_room.y == room.y and \
				   spawner_room.w == room.w and spawner_room.h == room.h:
					has_spawner_puzzle = true
					LogManager.log("DungeonGenerator: Skipping pre-spawned enemies for room (" + str(room.x) + ", " + str(room.y) + ") - has monster spawner puzzle", LogManager.CATEGORY_DUNGEON)
					break
			
			if not has_spawner_puzzle:
				var room_enemies = _place_enemies_in_room(room, grid, map_size, rng, level)
				all_enemies.append_array(room_enemies)
	
	# 9.5. Place interactable objects in rooms (excluding start and exit rooms)
	var all_interactable_objects = []
	for i in range(all_rooms.size()):
		var room = all_rooms[i]
		# Skip start room and exit room
		if i != start_room_index and i != exit_room_index:
			var room_objects = _place_interactable_objects_in_room(room, grid, map_size, all_doors, all_enemies, rng, room_puzzle_data)
			all_interactable_objects.append_array(room_objects)
	
	# 9.6. Place traps (1-2 per level, excluding start and exit rooms)
	var all_traps = _place_traps_in_dungeon(all_rooms, start_room_index, exit_room_index, grid, map_size, rng)
	
	# NOTE: Stairs placement was moved earlier (step 7.5, before torches) to prevent torch overlap
	# NOTE: Blocking doors placement was moved earlier (step 11, before enemy placement) to identify spawner puzzle rooms
	
	return {
		"rooms": all_rooms,
		"doors": all_doors,
		"torches": all_torches,
		"enemies": all_enemies,
		"interactable_objects": all_interactable_objects,
		"traps": all_traps,
		"stairs": stairs_data,
		"entrance": entrance_data,
		"blocking_doors": blocking_doors,
		"grid": grid,
		"tile_grid": tile_grid,
		"map_size": map_size,
		"start_room": all_rooms[start_room_index],
		"exit_room": all_rooms[exit_room_index]
	}

func _set_floor(room: Dictionary, grid: Array, tile_grid: Array, map_size: Vector2i, rng: RandomNumberGenerator):
	# Set floor tiles for the room (interior only, walls will be set separately)
	# Leave 2 tile border for walls (walls are 2 tiles tall)
	for x in range(room.x + 2, room.x + room.w - 2):
		for y in range(room.y + 2, room.y + room.h - 2):
			if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
				grid[x][y] = 1 # Floor
				# Random floor tile variation
				var floor_tile = FLOOR_TILES[rng.randi() % FLOOR_TILES.size()]
				tile_grid[x][y] = floor_tile

func _try_place_room_near(source_room: Dictionary, grid: Array, map_size: Vector2i, rng: RandomNumberGenerator) -> Dictionary:
	var attempts = 20
	while attempts > 0:
		var w = rng.randi_range(MIN_ROOM_SIZE, MAX_ROOM_SIZE)
		var h = rng.randi_range(MIN_ROOM_SIZE, MAX_ROOM_SIZE)
		
		# Try all four sides of the source room
		var sides = ["N", "S", "E", "W"]
		sides.shuffle()
		
		for side in sides:
			var x = source_room.x
			var y = source_room.y
			
			match side:
				"N":
					x = source_room.x + rng.randi_range(0, max(1, source_room.w - w))
					y = source_room.y - h - 4 # 4 tiles gap
				"S":
					x = source_room.x + rng.randi_range(0, max(1, source_room.w - w))
					y = source_room.y + source_room.h + 4
				"W":
					x = source_room.x - w - 4
					y = source_room.y + rng.randi_range(0, max(1, source_room.h - h))
				"E":
					x = source_room.x + source_room.w + 4
					y = source_room.y + rng.randi_range(0, max(1, source_room.h - h))
			
			var new_room = {"x": x, "y": y, "w": w, "h": h, "modifiers": []}
			if _is_valid_room_position(new_room, grid, map_size):
				return new_room
		
		attempts -= 1
	
	return {"x": 0, "y": 0, "w": 0, "h": 0, "modifiers": []}

func _is_valid_room_position(room: Dictionary, grid: Array, map_size: Vector2i) -> bool:
	# Check bounds with buffer
	if room.x < 4 or room.y < 4 or room.x + room.w >= map_size.x - 4 or room.y + room.h >= map_size.y - 4:
		return false
	
	# Check if room overlaps with existing rooms (with 4-tile buffer)
	for x in range(room.x - 4, room.x + room.w + 4):
		for y in range(room.y - 4, room.y + room.h + 4):
			if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
				if grid[x][y] != 0: # Not empty (wall)
					return false
	
	return true

func _connect_rooms(all_rooms: Array, grid: Array, tile_grid: Array, map_size: Vector2i, all_doors: Array, rng: RandomNumberGenerator):
	# First pass: connect each room to closest neighbors
	var connected_rooms = {}
	for room in all_rooms:
		connected_rooms[room] = []
	
	for room in all_rooms:
		var closest_rooms = _find_closest_rooms(room, all_rooms)
		var connection_attempts = 0
		var max_connections = 3
		
		for target_room in closest_rooms:
			if connection_attempts >= max_connections:
				break
			
			if not _rooms_are_connected(room, target_room, all_doors):
				var door = _create_corridor_between_rooms(room, target_room, grid, tile_grid, map_size, all_rooms, rng)
				if door.size() > 0:
					all_doors.append(door)
					connected_rooms[room].append(target_room)
					connected_rooms[target_room].append(room)
					connection_attempts += 1
	
	# Second pass: ensure all rooms are connected
	var attempts = 100
	while attempts > 0:
		var reachable = _find_reachable_rooms(all_rooms[0], all_rooms, all_doors)
		
		if reachable.size() == all_rooms.size():
			break
		
		# Find unreachable room and connect it
		for room in all_rooms:
			if not reachable.has(room):
				for target_room in reachable:
					var door = _create_corridor_between_rooms(room, target_room, grid, tile_grid, map_size, all_rooms, rng)
					if door.size() > 0:
						all_doors.append(door)
						break
				break
		
		attempts -= 1

func _create_corridor_between_rooms(room1: Dictionary, room2: Dictionary, grid: Array, tile_grid: Array, map_size: Vector2i, all_rooms: Array, rng: RandomNumberGenerator) -> Dictionary:
	var dx = abs(room2.x - room1.x)
	var dy = abs(room2.y - room1.y)
	
	# Check if rooms are too far apart
	if dx > 20 or dy > 20:
		return {}
	
	# Helper function to check if a corridor path intersects any room's floor area except room1 and room2
	# Corridors should not pass through the floor area of other rooms (walls are OK)
	var corridor_intersects_other_room = func(corridor_start_x: int, corridor_start_y: int, corridor_end_x: int, corridor_end_y: int, is_horizontal: bool) -> bool:
		if is_horizontal:
			# Horizontal corridor: check all tiles from start_x to end_x at y = corridor_y
			var corridor_y = corridor_start_y
			for check_x in range(min(corridor_start_x, corridor_end_x), max(corridor_start_x, corridor_end_x) + 1):
				# Check if this tile is inside any room's floor area (except room1 and room2)
				for other_room in all_rooms:
					if other_room == room1 or other_room == room2:
						continue
					# Check if the corridor tile is inside this room's floor area (excluding walls)
					# Floor area starts at room.x+2 and room.y+2 (after 2-tile wall) and ends before walls
					if check_x >= other_room.x + 2 and check_x < other_room.x + other_room.w - 2 and \
					   corridor_y >= other_room.y + 2 and corridor_y < other_room.y + other_room.h - 2:
						return true
		else:
			# Vertical corridor: check all tiles from start_y to end_y at x = corridor_x
			var corridor_x = corridor_start_x
			for check_y in range(min(corridor_start_y, corridor_end_y), max(corridor_start_y, corridor_end_y) + 1):
				# Check if this tile is inside any room's floor area (except room1 and room2)
				for other_room in all_rooms:
					if other_room == room1 or other_room == room2:
						continue
					# Check if the corridor tile is inside this room's floor area (excluding walls)
					# Floor area starts at room.x+2 and room.y+2 (after 2-tile wall) and ends before walls
					if corridor_x >= other_room.x + 2 and corridor_x < other_room.x + other_room.w - 2 and \
					   check_y >= other_room.y + 2 and check_y < other_room.y + other_room.h - 2:
						return true
		return false
	
	if dx > dy:
		# Horizontal corridor
		var left_room = room1 if room1.x < room2.x else room2
		var right_room = room2 if room1.x < room2.x else room1
		
		if right_room.x - (left_room.x + left_room.w) > 20:
			return {}
		
		# Door position - IN THE MIDDLE OF THE WALL (replacing part of the wall)
		# For horizontal doors (left/right), door is 2x3 tiles (2 wide, 3 tall)
		# The wall is 2 tiles wide (at left_room.x + left_room.w - 2 and left_room.x + left_room.w - 1)
		# Door must be at least 1 tile away from corners (corners are 2x2, so start at y+2)
		var wall_x = left_room.x + left_room.w - 1 # Right wall of left room (wall is at room.x + room.w - 1)
		var min_y = max(left_room.y + 2, right_room.y + 2) # At least 1 tile from top corner (corner is 2 tiles)
		var max_y = min(left_room.y + left_room.h - 5, right_room.y + right_room.h - 5) # At least 1 tile from bottom corner, and room for 3-tile tall door
		
		if max_y < min_y:
			return {}
		
		var door_y = min_y + rng.randi_range(0, max(0, max_y - min_y))
		var door_width = rng.randi_range(DOOR_MIN_WIDTH, DOOR_MAX_WIDTH)
		var corridor_length = right_room.x - (left_room.x + left_room.w)
		
		# Check if corridor path would intersect any other rooms
		var corridor_start_x = wall_x + 1
		var corridor_end_x = wall_x + corridor_length
		var corridor_y = door_y + 1
		if corridor_intersects_other_room.call(corridor_start_x, corridor_y, corridor_end_x, corridor_y, true):
			return {} # Corridor would pass through another room, skip this connection
		
		# Create corridor (1 tile wide) - use floor tiles
		# Corridor is between the rooms, after the door
		for x in range(wall_x + 1, wall_x + corridor_length + 1): # Corridor starts after the wall
			if x >= 0 and x < map_size.x and door_y + 1 >= 0 and door_y + 1 < map_size.y:
				grid[x][door_y + 1] = 3 # Corridor (middle row of door)
				var floor_tile = FLOOR_TILES[rng.randi() % FLOOR_TILES.size()]
				tile_grid[x][door_y + 1] = floor_tile
		
		# Create door on RIGHT wall of left room (2x3 tiles - 2 wide, 3 tall)
		# Door is placed ON the wall, replacing the 2-tile wide wall
		# The wall is at wall_x (left_room.x + left_room.w - 1) and wall_x - 1 (left_room.x + left_room.w - 2)
		# This is the RIGHT wall of the left room, so use DOOR_RIGHT_START (10,2 to 11,4)
		var door_start_x = wall_x - 1 # Door starts at the left wall tile (2 tiles wide)
		var door_start_y = door_y # Start from door_y position
		var door_tile_start = DOOR_RIGHT_START # Use RIGHT door for right wall (2x3: 10,2 to 11,4)
		for door_dx in range(2): # Door is 2 tiles wide
			for door_dy in range(3): # Door is 3 tiles tall
				var x = door_start_x + door_dx
				var y = door_start_y + door_dy
				if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
					grid[x][y] = 2 # Door area (replaces wall)
					# Use door tile coordinates (10,2) + offset for 2x3 door
					tile_grid[x][y] = door_tile_start + Vector2i(door_dx, door_dy)
					if door_dx == 0 and door_dy == 1:
						grid[x][y] = 1 # Floor
						var floor_tile = FLOOR_TILES[rng.randi() % FLOOR_TILES.size()]
						tile_grid[x][y] = floor_tile
					
		
		# Also create door on LEFT wall of right room (if there's a gap)
		if corridor_length > 0:
			var right_wall_x = right_room.x # Left wall of right room
			var right_door_start_x = right_wall_x # Door starts at the left wall tile (2 tiles wide)
			var right_door_tile_start = DOOR_LEFT_START # Use LEFT door for left wall (2x3: 5,2 to 6,4)
			for door_dx in range(2): # Door is 2 tiles wide
				for door_dy in range(3): # Door is 3 tiles tall
					var x = right_door_start_x + door_dx
					var y = door_start_y + door_dy
					if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
						grid[x][y] = 2 # Door area (replaces wall)
						# Use door tile coordinates (5,2) + offset for 2x3 door
						tile_grid[x][y] = right_door_tile_start + Vector2i(door_dx, door_dy)
						if door_dx == 1 and door_dy == 1:
							grid[x][y] = 1 # Floor
							var floor_tile = FLOOR_TILES[rng.randi() % FLOOR_TILES.size()]
							tile_grid[x][y] = floor_tile
		
		# CRITICAL: room1 = room the door is ON (left room for horizontal doors)
		#           room2 = room the door leads TO (right room for horizontal doors)
		# For a door on the RIGHT wall of the left room, room1 = left_room, room2 = right_room
		var door_room1 = left_room # Door is ON the left room's right wall
		var door_room2 = right_room # Door leads TO the right room
		
		return {
			"x": door_start_x,
			"y": door_y,
			"w": door_width,
			"h": 1,
			"dir": "E",
			"room1": door_room1, # CRITICAL: Door is IN the left room (on its right wall)
			"room2": door_room2 # Door leads TO the right room
		}
	else:
		# Vertical corridor
		var top_room = room1 if room1.y < room2.y else room2
		var bottom_room = room2 if room1.y < room2.y else room1
		
		if bottom_room.y - (top_room.y + top_room.h) > 20:
			return {}
		
		# Door position - IN THE MIDDLE OF THE WALL (replacing part of the wall)
		# For vertical doors (up/down), door is 3x2 tiles (3 wide, 2 tall)
		# The wall is 2 tiles tall (at top_room.y + top_room.h - 2 and top_room.y + top_room.h - 1)
		# Door must be at least 1 tile away from corners (corners are 2x2, so start at x+2)
		var wall_y = top_room.y + top_room.h - 1 # Bottom wall of top room (wall is at room.y + room.h - 1)
		var min_x = max(top_room.x + 2, bottom_room.x + 2) # At least 1 tile from left corner (corner is 2 tiles)
		var max_x = min(top_room.x + top_room.w - 5, bottom_room.x + bottom_room.w - 5) # At least 1 tile from right corner, and room for 3-tile wide door
		
		if max_x < min_x:
			return {}
		
		var door_x = min_x + rng.randi_range(0, max(0, max_x - min_x))
		var door_height = rng.randi_range(DOOR_MIN_WIDTH, DOOR_MAX_WIDTH)
		var corridor_length = bottom_room.y - (top_room.y + top_room.h)
		
		# Check if corridor path would intersect any other rooms
		var corridor_start_y = wall_y + 1
		var corridor_end_y = wall_y + corridor_length
		var corridor_x = door_x + 1
		if corridor_intersects_other_room.call(corridor_x, corridor_start_y, corridor_x, corridor_end_y, false):
			return {} # Corridor would pass through another room, skip this connection
		
		# Create corridor (1 tile wide) - use floor tiles
		# Corridor is between the rooms, after the door
		for y in range(wall_y + 1, wall_y + corridor_length + 1): # Corridor starts after the wall
			if door_x + 1 >= 0 and door_x + 1 < map_size.x and y >= 0 and y < map_size.y:
				grid[door_x + 1][y] = 3 # Corridor (middle column of door)
				var floor_tile = FLOOR_TILES[rng.randi() % FLOOR_TILES.size()]
				tile_grid[door_x + 1][y] = floor_tile
		
		# Create door on BOTTOM wall of top room (3x2 tiles - 3 wide, 2 tall)
		# Door is placed ON the wall, replacing the 2-tile tall wall
		# The wall is at wall_y (top_room.y + top_room.h - 1) and wall_y - 1 (top_room.y + top_room.h - 2)
		# This is the BOTTOM wall of the top room, so use DOOR_BOTTOM_START (7,5 to 9,6)
		var door_start_x = door_x # Start from door_x position
		var door_start_y = wall_y - 1 # Door starts at the upper wall tile (2 tiles tall)
		var door_tile_start = DOOR_BOTTOM_START # Use BOTTOM door for bottom wall (3x2: 7,5 to 9,6)
		for door_dx in range(3): # Door is 3 tiles wide
			for door_dy in range(2): # Door is 2 tiles tall
				var x = door_start_x + door_dx
				var y = door_start_y + door_dy
				if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
					grid[x][y] = 2 # Door area (replaces wall)
					# Use door tile coordinates (7,5) + offset for 3x2 door
					tile_grid[x][y] = door_tile_start + Vector2i(door_dx, door_dy)
					if door_dx == 1 and door_dy == 0:
						grid[x][y] = 1 # Floor
						var floor_tile = FLOOR_TILES[rng.randi() % FLOOR_TILES.size()]
						tile_grid[x][y] = floor_tile
		
		# Also create door on TOP wall of bottom room (if there's a gap)
		if corridor_length > 0:
			var bottom_wall_y = bottom_room.y # Top wall of bottom room
			var bottom_door_start_y = bottom_wall_y # Door starts at the top wall tile (2 tiles tall)
			var bottom_door_tile_start = DOOR_UP_START # Use UP door for top wall (3x2: 7,0 to 9,1)
			for door_dx in range(3): # Door is 3 tiles wide
				for door_dy in range(2): # Door is 2 tiles tall
					var x = door_start_x + door_dx
					var y = bottom_door_start_y + door_dy
					if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
						grid[x][y] = 2 # Door area (replaces wall)
						# Use door tile coordinates (7,0) + offset for 3x2 door
						tile_grid[x][y] = bottom_door_tile_start + Vector2i(door_dx, door_dy)
						if door_dx == 1 and door_dy == 1:
							grid[x][y] = 1 # Floor
							var floor_tile = FLOOR_TILES[rng.randi() % FLOOR_TILES.size()]
							tile_grid[x][y] = floor_tile
		
		# CRITICAL: room1 = room the door is ON (top room for vertical doors)
		#           room2 = room the door leads TO (bottom room for vertical doors)
		# For a door on the BOTTOM wall of the top room, room1 = top_room, room2 = bottom_room
		var door_room1 = top_room # Door is ON the top room's bottom wall
		var door_room2 = bottom_room # Door leads TO the bottom room
		
		return {
			"x": door_x,
			"y": door_start_y,
			"w": 1,
			"h": door_height,
			"dir": "S",
			"room1": door_room1, # CRITICAL: Door is IN the top room (on its bottom wall)
			"room2": door_room2 # Door leads TO the bottom room
		}

func _find_closest_rooms(room: Dictionary, all_rooms: Array) -> Array:
	if all_rooms.size() <= 1:
		return []
	
	var distances = []
	for other in all_rooms:
		if other == room:
			continue
		var dist = abs(room.x - other.x) + abs(room.y - other.y)
		distances.append({"room": other, "distance": dist})
	
	if distances.size() > 0:
		distances.sort_custom(func(a, b): return a.distance < b.distance)
		return distances.map(func(item): return item.room)
	
	return []

func _rooms_are_connected(room1: Dictionary, room2: Dictionary, doors: Array) -> bool:
	for door in doors:
		if (door.room1 == room1 and door.room2 == room2) or \
		   (door.room1 == room2 and door.room2 == room1):
			return true
	return false

func _place_torches_in_room(room: Dictionary, grid: Array, all_doors: Array, _map_size: Vector2i, rng: RandomNumberGenerator) -> Array:
	# Place 0 to 4 torches randomly on walls of the room
	# Torches cannot be placed exactly on doors
	var torches = []
	var num_torches = rng.randi_range(0, 4)
	
	# Collect valid wall positions (excluding doors)
	var valid_wall_positions = []
	var tile_size = 16
	
	# Calculate the torch Y position relative to floor (8 pixels down from top wall center)
	# This ensures all torches have the same distance to the floor
	var torch_y_offset = 8 # Pixels down from top wall center
	var torch_y_from_floor = room.y * tile_size + tile_size / 2.0 + torch_y_offset
	
	# Top wall (y = room.y, x from room.x+2 to room.x+room.w-2, excluding corners)
	for x in range(room.x + 2, room.x + room.w - 2):
		if _is_valid_torch_position(x, room.y, grid, all_doors):
			var world_pos = Vector2(x * tile_size + tile_size / 2.0, torch_y_from_floor)
			valid_wall_positions.append({"pos": world_pos, "wall": "top", "rotation": 0})
	
	# Bottom wall (y = room.y + room.h - 1, x from room.x+2 to room.x+room.w-2)
	# Place torches at the same distance from floor as top wall torches
	# Bottom wall center is at: (room.y + room.h - 1) * tile_size + tile_size / 2.0
	# To get same distance from floor as top wall: room.y * tile_size + tile_size / 2.0 + 8
	# But we need to place it on the bottom wall, so use bottom wall's Y position
	# Calculate: bottom_wall_y_world - (room.h * tile_size) + torch_y_offset
	# This gives us the same distance from floor
	# Bottom wall torches should be 8 pixels up from bottom wall center (closer to floor)
	# Bottom wall center is at: (room.y + room.h - 1) * tile_size + tile_size / 2.0
	# To get same distance from floor as top wall: move up 8 pixels from bottom wall center
	var bottom_wall_center_y = (room.y + room.h - 1) * tile_size + tile_size / 2.0
	var bottom_torch_y = bottom_wall_center_y - torch_y_offset # Move up 8 pixels from bottom wall center
	for x in range(room.x + 2, room.x + room.w - 2):
		# Check if this is a valid bottom wall position (check the lower part of bottom wall)
		var bottom_wall_y = room.y + room.h - 1
		if _is_valid_torch_position(x, bottom_wall_y, grid, all_doors):
			# Place at bottom wall Y position, but 8 pixels up from center (closer to floor)
			var world_pos = Vector2(x * tile_size + tile_size / 2.0, bottom_torch_y)
			valid_wall_positions.append({"pos": world_pos, "wall": "bottom", "rotation": 180})
	
	# Left wall (2 tiles wide: room.x and room.x + 1)
	# Place torches at the same Y position as top wall torches (same distance from floor)
	# torch_y_from_floor = room.y * tile_size + tile_size / 2.0 + 8
	# Left wall has two parts: room.x (left part, also corner) and room.x + 1 (right part, actual wall)
	# We should place torches on room.x + 1 (the right part of the 2-tile wide left wall), not on room.x (corner)
	# Find which Y tile position matches torch_y_from_floor
	# torch_y_from_floor = room.y * tile_size + 8 + 8 = room.y * tile_size + 16
	# target_y_tile = int((room.y * 16 + 16) / 16) = room.y + 1
	# But we need to check if room.y + 1 is in the valid range (room.y + 2 to room.y + room.h - 2)
	# Actually, torch_y_from_floor is 8 pixels down from top wall center, which is still in the top wall tile
	# So target_y_tile = room.y + 1, but we need to check if it's >= room.y + 2 (excluding corners)
	# Since target_y_tile = room.y + 1, and we check >= room.y + 2, it will always fail!
	# We need to check all valid Y positions on the left/right walls and place at torch_y_from_floor
	# Left/right walls are valid from room.y + 2 to room.y + room.h - 2 (skipping 2-tile corners)
	for y in range(room.y + 2, room.y + room.h - 2):
		# First check if there's a door at this Y position on the left wall
		# Left door (dir="W") is 3 tiles tall, so check if y is in any door's Y range
		var has_door_at_y = false
		for door in all_doors:
			var door_dir = door.dir if "dir" in door else ""
			if door_dir == "W" and door.x == room.x:
				# Left door at room.x, check if y is within door's Y range (door.y to door.y + 3)
				if y >= door.y and y <= door.y + 3:
					has_door_at_y = true
					break
		if has_door_at_y:
			continue # Skip this Y position if there's a door here
		
		# Check if this is a valid left wall position
		# Left wall has 2 tiles: room.x and room.x + 1
		# Check both tiles to ensure we're not placing on a door
		# Use room.x + 1 (the right part of the left wall) for torch placement
		if _is_valid_torch_position(room.x, y, grid, all_doors) and _is_valid_torch_position(room.x + 1, y, grid, all_doors):
			# Calculate torch world position
			# X position is on the left wall: (room.x + 1) * tile_size + tile_size / 2.0
			# Move it further to the left (negative X) to position it better on the wall
			var left_wall_x = (room.x + 1) * tile_size + tile_size / 2.0 - 8 # Move 8 pixels to the left
			# Y position should be at the same distance from floor: y * tile_size + tile_size / 2.0 + 8
			var left_wall_y = y * tile_size + tile_size / 2.0 + torch_y_offset
			var world_pos = Vector2(left_wall_x, left_wall_y)
			
			# CRITICAL: Check if torch's 16x16 pixel bounding box overlaps with any door
			# Torch is 16x16 pixels, so it extends 8 pixels in each direction from its center
			# Torch bounding box: from (left_wall_x - 8, left_wall_y - 8) to (left_wall_x + 8, left_wall_y + 8)
			var torch_bbox_min_x = left_wall_x - 8
			var torch_bbox_max_x = left_wall_x + 8
			var torch_bbox_min_y = left_wall_y - 8
			var torch_bbox_max_y = left_wall_y + 8
			
			# Check if torch bounding box overlaps with any door's bounding box
			# Only check doors that are on the left wall of this room (dir="W")
			var overlaps_door = false
			var tile_size_check = 16
			for door in all_doors:
				var door_dir = door.dir if "dir" in door else ""
				if door_dir != "W": # Only check left doors (dir="W")
					continue
				
				# Check if this door is on the left wall of this room
				# Left door is on the left wall, so door.x should be room.x (the left tile of the left wall)
				var door_x_match = (door.x == room.x)
				if not door_x_match:
					continue # This door is not on this room's left wall
				
				# Left door (dir="W"): 2 tiles wide, 3 tiles tall
				# Door position is at door.x, door.y (upper-left tile)
				# Door occupies tiles: x from door.x to door.x + 2, y from door.y to door.y + 3
				# Door world bounding box: from (door.x * 16, door.y * 16) to ((door.x + 2) * 16, (door.y + 3) * 16)
				# CRITICAL: A torch is 16x16 pixels (8px in each direction), so expand door bounds by 8px in ALL directions
				# Door occupies columns room.x (0) and room.x + 1 (1), so torch at room.x + 1 can overlap if it extends left
				var door_min_x = door.x * tile_size_check - 8
				var door_max_x = (door.x + 2) * tile_size_check + 8
				var door_min_y = door.y * tile_size_check - 8
				var door_max_y = (door.y + 3) * tile_size_check + 8
				
				# Check if torch bounding box overlaps with door bounding box (non-overlapping means torch is safe)
				# Overlap exists if NOT (torch_max < door_min OR torch_min > door_max)
				var x_overlap = not (torch_bbox_max_x < door_min_x or torch_bbox_min_x > door_max_x)
				var y_overlap = not (torch_bbox_max_y < door_min_y or torch_bbox_min_y > door_max_y)
				if x_overlap and y_overlap:
					overlaps_door = true
					break
			
			if not overlaps_door:
				valid_wall_positions.append({"pos": world_pos, "wall": "left", "rotation": 270})
				break # Only add one torch per wall
	
	# Right wall (2 tiles wide: room.x + room.w - 2 and room.x + room.w - 1)
	# Place torches at the same distance from floor as top wall torches
	# Right wall has two parts: room.x + room.w - 2 (left part, actual wall) and room.x + room.w - 1 (right part, also corner)
	# We should place torches on room.x + room.w - 2 (the left part of the 2-tile wide right wall), not on room.x + room.w - 1 (corner)
	# Check all valid Y positions on the right wall
	# CRITICAL: Check both tiles of the right wall (similar to left wall) to ensure we're not placing on a door
	for y in range(room.y + 2, room.y + room.h - 2):
		# Check if this is a valid right wall position
		# Right wall has 2 tiles: room.x + room.w - 2 (left part) and room.x + room.w - 1 (right part/corner)
		# Check both tiles to ensure we're not placing on a door
		# Use room.x + room.w - 2 (the left part of the right wall) for torch placement
		if _is_valid_torch_position(room.x + room.w - 2, y, grid, all_doors) and _is_valid_torch_position(room.x + room.w - 1, y, grid, all_doors):
			# Calculate torch world position
			# X position is on the right wall: (room.x + room.w - 2) * tile_size + tile_size / 2.0
			# Move it further to the right (positive X) to position it better on the wall
			var right_wall_x = (room.x + room.w - 2) * tile_size + tile_size / 2.0 + 8 # Move 8 pixels to the right
			# Y position should be at the same distance from floor: y * tile_size + tile_size / 2.0 + 8
			var right_wall_y = y * tile_size + tile_size / 2.0 + torch_y_offset
			var world_pos = Vector2(right_wall_x, right_wall_y)
			
			# CRITICAL: Check if torch's 16x16 pixel bounding box overlaps with any door
			# Torch is 16x16 pixels, so it extends 8 pixels in each direction from its center
			# Torch bounding box: from (right_wall_x - 8, right_wall_y - 8) to (right_wall_x + 8, right_wall_y + 8)
			var torch_bbox_min_x = right_wall_x - 8
			var torch_bbox_max_x = right_wall_x + 8
			var torch_bbox_min_y = right_wall_y - 8
			var torch_bbox_max_y = right_wall_y + 8
			
			# Check if torch bounding box overlaps with any door's bounding box
			# Only check doors that are on the right wall of this room (dir="E")
			var overlaps_door = false
			var tile_size_check = 16
			for door in all_doors:
				var door_dir = door.dir if "dir" in door else ""
				if door_dir != "E": # Only check right doors (dir="E")
					continue
				
				# Check if this door is on the right wall of this room
				# Right door is on the right wall, so door.x should be room.x + room.w - 2 (the left part of the right wall)
				var door_x_match = (door.x == room.x + room.w - 2)
				if not door_x_match:
					continue # This door is not on this room's right wall
				
				# Right door (dir="E"): 2 tiles wide, 3 tiles tall
				# Door position is at door.x, door.y (upper-left tile)
				# Door occupies tiles: x from door.x to door.x + 2, y from door.y to door.y + 3
				# Door world bounding box: from (door.x * 16, door.y * 16) to ((door.x + 2) * 16, (door.y + 3) * 16)
				var door_min_x = door.x * tile_size_check
				var door_max_x = (door.x + 2) * tile_size_check
				var door_min_y = door.y * tile_size_check
				var door_max_y = (door.y + 3) * tile_size_check
				
				# Check if torch bounding box overlaps with door bounding box
				if not (torch_bbox_max_x < door_min_x or torch_bbox_min_x > door_max_x or \
						torch_bbox_max_y < door_min_y or torch_bbox_min_y > door_max_y):
					overlaps_door = true
					break
			
			if not overlaps_door:
				valid_wall_positions.append({"pos": world_pos, "wall": "right", "rotation": 90})
				break # Only add one torch per wall
	
	# Randomly select torch positions
	if valid_wall_positions.size() == 0:
		return torches
	
	valid_wall_positions.shuffle()
	for i in range(min(num_torches, valid_wall_positions.size())):
		var torch_data = valid_wall_positions[i]
		torches.append({
			"position": torch_data.pos,
			"rotation": torch_data.rotation
		})
	
	return torches

func _is_valid_torch_position(x: int, y: int, grid: Array, all_doors: Array) -> bool:
	# Check if position is within bounds
	if x < 0 or y < 0:
		return false
	
	if x >= grid.size() or y >= grid[x].size():
		return false
	
	# Check grid value - torches can only be placed on wall tiles (grid_value == 0)
	# Cannot place on: doors (2), corridors (3), stairs (4), or floor (1)
	var grid_value = grid[x][y]
	if grid_value != 0: # Only wall tiles (0) are valid for torches
		# Specifically check for stairs (4) to prevent overlap
		if grid_value == 4:
			return false # Stairs tile
		if grid_value == 2:
			return false # Door tile
		# Any other non-wall value is invalid
		return false
	
	# Also check if position is within door area from door dictionaries
	# This is a backup check in case door grid values aren't set yet
	for door in all_doors:
		var door_x = door.x
		var door_y = door.y
		var door_dir = door.dir if "dir" in door else ""
		
		# Calculate actual door dimensions based on direction
		# Horizontal doors (E/W): actually 2-3 tiles wide and 3 tiles tall in grid
		# Vertical doors (N/S): actually 3 tiles wide and 2-3 tiles tall in grid
		var door_w = door.w if "w" in door else 2
		var door_h = door.h if "h" in door else 3
		var actual_w = door_w
		var actual_h = door_h
		
		if door_dir == "E" or door_dir == "W":
			# Horizontal door: w is correct (2 or 3), but h is actually 3 in grid (not 1)
			actual_h = 3
		elif door_dir == "N" or door_dir == "S":
			# Vertical door: h is correct (2 or 3), but w is actually 3 in grid (not 1)
			actual_w = 3
		
		# Check if (x, y) is within door area
		if x >= door_x and x < door_x + actual_w and y >= door_y and y < door_y + actual_h:
			return false
	
	return true

func _find_reachable_rooms(start_room: Dictionary, _all_rooms: Array, all_doors: Array) -> Array:
	var reachable = [start_room]
	var queue = [start_room]
	
	# Helper function to check if a room is already in the reachable list (value-based comparison)
	var is_room_in_list = func(room_list: Array, room: Dictionary) -> bool:
		if not room or room.is_empty():
			return false
		for r in room_list:
			if r.x == room.x and r.y == room.y and r.w == room.w and r.h == room.h:
				return true
		return false
	
	while queue.size() > 0:
		var current = queue.pop_front()
		for door in all_doors:
			var next_room = null
			# Use value-based comparison for room matching
			if "room1" in door and door.room1 is Dictionary:
				if door.room1.x == current.x and door.room1.y == current.y and \
				   door.room1.w == current.w and door.room1.h == current.h:
					next_room = door.room2 if "room2" in door else null
			if next_room == null and "room2" in door and door.room2 is Dictionary:
				if door.room2.x == current.x and door.room2.y == current.y and \
				   door.room2.w == current.w and door.room2.h == current.h:
					next_room = door.room1 if "room1" in door else null
			
			if next_room != null and next_room is Dictionary and not next_room.is_empty():
				if not is_room_in_list.call(reachable, next_room):
					reachable.append(next_room)
					queue.append(next_room)
	
	return reachable

func _add_hole_to_room(room: Dictionary, grid: Array, tile_grid: Array, map_size: Vector2i, rng: RandomNumberGenerator):
	# Add a hole (minimum 9x9 tiles) somewhere in the room
	# Holes should only be placed in the floor area (not in walls)
	# Floor area is from room.x + 2 to room.x + room.w - 2, and room.y + 2 to room.y + room.h - 2
	var floor_min_x = room.x + 2
	var floor_min_y = room.y + 2
	var floor_max_x = room.x + room.w - 2
	var floor_max_y = room.y + room.h - 2
	var floor_w = floor_max_x - floor_min_x
	var floor_h = floor_max_y - floor_min_y
	
	if floor_w < MIN_HOLE_SIZE or floor_h < MIN_HOLE_SIZE:
		return # Room too small for hole
	
	var hole_size = rng.randi_range(MIN_HOLE_SIZE, min(floor_w, floor_h, 12))
	
	# Position hole within floor area (with 1 tile margin from floor edges)
	var max_x = floor_max_x - hole_size
	var max_y = floor_max_y - hole_size
	
	if max_x < floor_min_x or max_y < floor_min_y:
		return # Floor area too small for hole
	
	var hole_x = rng.randi_range(floor_min_x, max_x)
	var hole_y = rng.randi_range(floor_min_y, max_y)
	
	# Create hole (back to wall) - use inner wall tiles
	# Only create hole if the position is currently a floor tile
	for x in range(hole_x, hole_x + hole_size):
		for y in range(hole_y, hole_y + hole_size):
			if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
				# Only create hole if it's currently a floor tile
				if grid[x][y] == 1: # Floor
					grid[x][y] = 0 # Wall
					# Use inner wall tiles for holes
					if x == hole_x and y == hole_y:
						tile_grid[x][y] = INNER_WALL_TOP_LEFT
					elif x == hole_x + hole_size - 1 and y == hole_y:
						tile_grid[x][y] = INNER_WALL_TOP_RIGHT
					elif x == hole_x and y == hole_y + hole_size - 1:
						tile_grid[x][y] = INNER_WALL_BOTTOM_LEFT
					elif x == hole_x + hole_size - 1 and y == hole_y + hole_size - 1:
						tile_grid[x][y] = INNER_WALL_BOTTOM_RIGHT
					else:
						# Use default wall tile for interior of hole
						tile_grid[x][y] = WALL_TOP_UPPER

func _find_farthest_room(all_rooms: Array, start_index: int) -> int:
	var start_room = all_rooms[start_index]
	var max_distance = 0
	var farthest_index = 0
	
	for i in range(all_rooms.size()):
		if i == start_index:
			continue
		var room = all_rooms[i]
		var distance = abs(room.x - start_room.x) + abs(room.y - start_room.y)
		if distance > max_distance:
			max_distance = distance
			farthest_index = i
	
	return farthest_index

func _find_farthest_room_from_list(all_rooms: Array, start_index: int, reachable_rooms: Array) -> int:
	# Find the farthest room from the start room, but only from the reachable rooms list
	var start_room = all_rooms[start_index]
	var max_distance = 0
	var farthest_index = -1
	
	for i in range(all_rooms.size()):
		if i == start_index:
			continue
		var room = all_rooms[i]
		# Only consider reachable rooms
		if not reachable_rooms.has(room):
			continue
		
		var distance = abs(room.x - start_room.x) + abs(room.y - start_room.y)
		if distance > max_distance:
			max_distance = distance
			farthest_index = i
	
	return farthest_index

func _render_room_walls(all_rooms: Array, grid: Array, tile_grid: Array, map_size: Vector2i, _rng: RandomNumberGenerator):
	# Render walls around each room
	# All walls are 2 tiles tall (upper and lower parts)
	# Walls are placed at room boundaries (room.x, room.y, etc.)
	# IMPORTANT: Only render walls where grid[x][y] == 0 (wall) or grid[x][y] == 2 (door)
	# Do NOT overwrite floor tiles (grid[x][y] == 1) or corridors (grid[x][y] == 3)
	for room in all_rooms:
		# Top-left corner (2x2): (room.x, room.y), (room.x+1, room.y), (room.x, room.y+1), (room.x+1, room.y+1)
		# Only render if it's a wall (0) or door (2), not floor (1) or corridor (3)
		if room.x >= 0 and room.x < map_size.x and room.y >= 0 and room.y < map_size.y:
			if grid[room.x][room.y] == 0 or grid[room.x][room.y] == 2: # Wall or door
				if grid[room.x][room.y] != 2: # Not a door (don't overwrite door tiles)
					tile_grid[room.x][room.y] = WALL_TOP_LEFT_TOP_LEFT
		if room.x + 1 >= 0 and room.x + 1 < map_size.x and room.y >= 0 and room.y < map_size.y:
			if grid[room.x + 1][room.y] == 0 or grid[room.x + 1][room.y] == 2: # Wall or door
				if grid[room.x + 1][room.y] != 2: # Not a door
					tile_grid[room.x + 1][room.y] = WALL_TOP_LEFT_TOP_RIGHT
		if room.x >= 0 and room.x < map_size.x and room.y + 1 >= 0 and room.y + 1 < map_size.y:
			if grid[room.x][room.y + 1] == 0 or grid[room.x][room.y + 1] == 2: # Wall or door
				if grid[room.x][room.y + 1] != 2: # Not a door
					tile_grid[room.x][room.y + 1] = WALL_TOP_LEFT_BOTTOM_LEFT
		if room.x + 1 >= 0 and room.x + 1 < map_size.x and room.y + 1 >= 0 and room.y + 1 < map_size.y:
			if grid[room.x + 1][room.y + 1] == 0 or grid[room.x + 1][room.y + 1] == 2: # Wall or door
				if grid[room.x + 1][room.y + 1] != 2: # Not a door
					tile_grid[room.x + 1][room.y + 1] = WALL_TOP_LEFT_BOTTOM_RIGHT
		
		# Top-right corner (2x2): (room.x+room.w-2, room.y), (room.x+room.w-1, room.y), (room.x+room.w-2, room.y+1), (room.x+room.w-1, room.y+1)
		# Only render if it's a wall (0) or door (2), not floor (1) or corridor (3)
		var top_right_x_left = room.x + room.w - 2
		var top_right_x_right = room.x + room.w - 1
		if top_right_x_left >= 0 and top_right_x_left < map_size.x and room.y >= 0 and room.y < map_size.y:
			if grid[top_right_x_left][room.y] == 0 or grid[top_right_x_left][room.y] == 2: # Wall or door
				if grid[top_right_x_left][room.y] != 2: # Not a door (don't overwrite door tiles)
					tile_grid[top_right_x_left][room.y] = WALL_TOP_RIGHT_TOP_LEFT
		if top_right_x_right >= 0 and top_right_x_right < map_size.x and room.y >= 0 and room.y < map_size.y:
			if grid[top_right_x_right][room.y] == 0 or grid[top_right_x_right][room.y] == 2: # Wall or door
				if grid[top_right_x_right][room.y] != 2: # Not a door
					tile_grid[top_right_x_right][room.y] = WALL_TOP_RIGHT_TOP_RIGHT
		if top_right_x_left >= 0 and top_right_x_left < map_size.x and room.y + 1 >= 0 and room.y + 1 < map_size.y:
			if grid[top_right_x_left][room.y + 1] == 0 or grid[top_right_x_left][room.y + 1] == 2: # Wall or door
				if grid[top_right_x_left][room.y + 1] != 2: # Not a door
					tile_grid[top_right_x_left][room.y + 1] = WALL_TOP_RIGHT_BOTTOM_LEFT
		if top_right_x_right >= 0 and top_right_x_right < map_size.x and room.y + 1 >= 0 and room.y + 1 < map_size.y:
			if grid[top_right_x_right][room.y + 1] == 0 or grid[top_right_x_right][room.y + 1] == 2: # Wall or door
				if grid[top_right_x_right][room.y + 1] != 2: # Not a door
					tile_grid[top_right_x_right][room.y + 1] = WALL_TOP_RIGHT_BOTTOM_RIGHT
		
		# Top wall (2 tiles tall) - between corners
		for x in range(room.x + 2, room.x + room.w - 2):
			# Upper part of top wall (at room.y)
			if x >= 0 and x < map_size.x and room.y >= 0 and room.y < map_size.y:
				# Only render wall if it's a wall (0) or door (2), not floor (1) or corridor (3)
				if grid[x][room.y] == 0 or grid[x][room.y] == 2: # Wall or door
					if grid[x][room.y] != 2: # Not a door (don't overwrite door tiles)
						tile_grid[x][room.y] = WALL_TOP_UPPER
			
			# Lower part of top wall (at room.y + 1)
			if x >= 0 and x < map_size.x and room.y + 1 >= 0 and room.y + 1 < map_size.y:
				# Only render wall if it's a wall (0) or door (2), not floor (1) or corridor (3)
				if grid[x][room.y + 1] == 0 or grid[x][room.y + 1] == 2: # Wall or door
					if grid[x][room.y + 1] != 2: # Not a door (don't overwrite door tiles)
						tile_grid[x][room.y + 1] = WALL_TOP_LOWER
		
		# Bottom-left corner (2x2): (room.x, room.y+room.h-2), (room.x+1, room.y+room.h-2), (room.x, room.y+room.h-1), (room.x+1, room.y+room.h-1)
		# Only render if it's a wall (0) or door (2), not floor (1) or corridor (3)
		var bottom_y = room.y + room.h - 1
		var bottom_y_upper = room.y + room.h - 2
		if room.x >= 0 and room.x < map_size.x and bottom_y_upper >= 0 and bottom_y_upper < map_size.y:
			if grid[room.x][bottom_y_upper] == 0 or grid[room.x][bottom_y_upper] == 2: # Wall or door
				if grid[room.x][bottom_y_upper] != 2: # Not a door (don't overwrite door tiles)
					tile_grid[room.x][bottom_y_upper] = WALL_BOTTOM_LEFT_TOP_LEFT
		if room.x + 1 >= 0 and room.x + 1 < map_size.x and bottom_y_upper >= 0 and bottom_y_upper < map_size.y:
			if grid[room.x + 1][bottom_y_upper] == 0 or grid[room.x + 1][bottom_y_upper] == 2: # Wall or door
				if grid[room.x + 1][bottom_y_upper] != 2: # Not a door
					tile_grid[room.x + 1][bottom_y_upper] = WALL_BOTTOM_LEFT_TOP_RIGHT
		if room.x >= 0 and room.x < map_size.x and bottom_y >= 0 and bottom_y < map_size.y:
			if grid[room.x][bottom_y] == 0 or grid[room.x][bottom_y] == 2: # Wall or door
				if grid[room.x][bottom_y] != 2: # Not a door
					tile_grid[room.x][bottom_y] = WALL_BOTTOM_LEFT_BOTTOM_LEFT
		if room.x + 1 >= 0 and room.x + 1 < map_size.x and bottom_y >= 0 and bottom_y < map_size.y:
			if grid[room.x + 1][bottom_y] == 0 or grid[room.x + 1][bottom_y] == 2: # Wall or door
				if grid[room.x + 1][bottom_y] != 2: # Not a door
					tile_grid[room.x + 1][bottom_y] = WALL_BOTTOM_LEFT_BOTTOM_RIGHT
		
		# Bottom-right corner (2x2): (room.x+room.w-2, room.y+room.h-2), (room.x+room.w-1, room.y+room.h-2), (room.x+room.w-2, room.y+room.h-1), (room.x+room.w-1, room.y+room.h-1)
		# Only render if it's a wall (0) or door (2), not floor (1) or corridor (3)
		var bottom_right_x_left = room.x + room.w - 2
		var bottom_right_x_right = room.x + room.w - 1
		if bottom_right_x_left >= 0 and bottom_right_x_left < map_size.x and bottom_y_upper >= 0 and bottom_y_upper < map_size.y:
			if grid[bottom_right_x_left][bottom_y_upper] == 0 or grid[bottom_right_x_left][bottom_y_upper] == 2: # Wall or door
				if grid[bottom_right_x_left][bottom_y_upper] != 2: # Not a door (don't overwrite door tiles)
					tile_grid[bottom_right_x_left][bottom_y_upper] = WALL_BOTTOM_RIGHT_TOP_LEFT
		if bottom_right_x_right >= 0 and bottom_right_x_right < map_size.x and bottom_y_upper >= 0 and bottom_y_upper < map_size.y:
			if grid[bottom_right_x_right][bottom_y_upper] == 0 or grid[bottom_right_x_right][bottom_y_upper] == 2: # Wall or door
				if grid[bottom_right_x_right][bottom_y_upper] != 2: # Not a door
					tile_grid[bottom_right_x_right][bottom_y_upper] = WALL_BOTTOM_RIGHT_TOP_RIGHT
		if bottom_right_x_left >= 0 and bottom_right_x_left < map_size.x and bottom_y >= 0 and bottom_y < map_size.y:
			if grid[bottom_right_x_left][bottom_y] == 0 or grid[bottom_right_x_left][bottom_y] == 2: # Wall or door
				if grid[bottom_right_x_left][bottom_y] != 2: # Not a door
					tile_grid[bottom_right_x_left][bottom_y] = WALL_BOTTOM_RIGHT_BOTTOM_LEFT
		if bottom_right_x_right >= 0 and bottom_right_x_right < map_size.x and bottom_y >= 0 and bottom_y < map_size.y:
			if grid[bottom_right_x_right][bottom_y] == 0 or grid[bottom_right_x_right][bottom_y] == 2: # Wall or door
				if grid[bottom_right_x_right][bottom_y] != 2: # Not a door
					tile_grid[bottom_right_x_right][bottom_y] = WALL_BOTTOM_RIGHT_BOTTOM_RIGHT
		
		# Bottom wall (2 tiles tall) - between corners
		for x in range(room.x + 2, room.x + room.w - 2):
			# Upper part of bottom wall (at room.y + room.h - 2)
			if x >= 0 and x < map_size.x and bottom_y_upper >= 0 and bottom_y_upper < map_size.y:
				# Only render wall if it's a wall (0) or door (2), not floor (1) or corridor (3)
				if grid[x][bottom_y_upper] == 0 or grid[x][bottom_y_upper] == 2: # Wall or door
					if grid[x][bottom_y_upper] != 2: # Not a door (don't overwrite door tiles)
						tile_grid[x][bottom_y_upper] = WALL_BOTTOM_UPPER
			
			# Lower part of bottom wall (at room.y + room.h - 1)
			if x >= 0 and x < map_size.x and bottom_y >= 0 and bottom_y < map_size.y:
				# Only render wall if it's a wall (0) or door (2), not floor (1) or corridor (3)
				if grid[x][bottom_y] == 0 or grid[x][bottom_y] == 2: # Wall or door
					if grid[x][bottom_y] != 2: # Not a door (don't overwrite door tiles)
						tile_grid[x][bottom_y] = WALL_BOTTOM_LOWER
		
		# Left wall (2 tiles wide) - at room.x and room.x + 1 (left and right parts)
		for y in range(room.y + 2, room.y + room.h - 2): # Skip corners (first 2 and last 2 rows)
			# Left part of left wall (at room.x)
			if room.x >= 0 and room.x < map_size.x and y >= 0 and y < map_size.y:
				# Only render wall if it's a wall (0) or door (2), not floor (1) or corridor (3)
				if grid[room.x][y] == 0 or grid[room.x][y] == 2: # Wall or door
					if grid[room.x][y] != 2: # Not a door (don't overwrite door tiles)
						tile_grid[room.x][y] = WALL_LEFT_LEFT
			
			# Right part of left wall (at room.x + 1)
			if room.x + 1 >= 0 and room.x + 1 < map_size.x and y >= 0 and y < map_size.y:
				# Only render wall if it's a wall (0) or door (2), not floor (1) or corridor (3)
				if grid[room.x + 1][y] == 0 or grid[room.x + 1][y] == 2: # Wall or door
					if grid[room.x + 1][y] != 2: # Not a door (don't overwrite door tiles)
						tile_grid[room.x + 1][y] = WALL_LEFT_RIGHT
		
		# Right wall (2 tiles wide) - at room.x + room.w - 2 and room.x + room.w - 1 (left and right parts)
		var right_x_left = room.x + room.w - 2
		var right_x_right = room.x + room.w - 1
		for y in range(room.y + 2, room.y + room.h - 2): # Skip corners (first 2 and last 2 rows)
			# Left part of right wall (at room.x + room.w - 2)
			if right_x_left >= 0 and right_x_left < map_size.x and y >= 0 and y < map_size.y:
				# Only render wall if it's a wall (0) or door (2), not floor (1) or corridor (3)
				if grid[right_x_left][y] == 0 or grid[right_x_left][y] == 2: # Wall or door
					if grid[right_x_left][y] != 2: # Not a door (don't overwrite door tiles)
						tile_grid[right_x_left][y] = WALL_RIGHT_LEFT
			
			# Right part of right wall (at room.x + room.w - 1)
			if right_x_right >= 0 and right_x_right < map_size.x and y >= 0 and y < map_size.y:
				# Only render wall if it's a wall (0) or door (2), not floor (1) or corridor (3)
				if grid[right_x_right][y] == 0 or grid[right_x_right][y] == 2: # Wall or door
					if grid[right_x_right][y] != 2: # Not a door (don't overwrite door tiles)
						tile_grid[right_x_right][y] = WALL_RIGHT_RIGHT

func _place_enemies_in_room(room: Dictionary, grid: Array, map_size: Vector2i, rng: RandomNumberGenerator, level: int = 1) -> Array:
	# Place enemies in a room, scaled by level
	# Level 1: 0-2 enemies per room (fewer)
	# Level 2+: 0-4 enemies per room, increasing max with level
	# Returns array of enemy data dictionaries
	var enemies = []
	
	# Calculate max enemies based on level
	# Level 1: max 2 enemies per room (fewer for first level)
	# Level 2: max 3 enemies per room
	# Level 3: max 4 enemies per room
	# Level 4+: max 5-6 enemies per room (scales with level)
	var max_enemies = 2 if level == 1 else min(1 + level, 6) # Level 1: 2, Level 2: 3, Level 3: 4, Level 4: 5, Level 5+: 6
	var num_enemies = rng.randi_range(0, max_enemies)
	
	# Available enemy types (scene paths)
	var enemy_types = [
		"res://scenes/enemy_rat.tscn",
		"res://scenes/enemy_humanoid.tscn",
		"res://scenes/enemy_slime.tscn",
		"res://scenes/enemy_bat.tscn",
		"res://scenes/enemy_hand.tscn"
	]
	
	# Find valid floor positions in the room (excluding walls)
	var valid_positions = []
	var tile_size = 16
	
	# Room interior is from room.x + 2 to room.x + room.w - 2 (excluding 2-tile walls)
	for x in range(room.x + 2, room.x + room.w - 2):
		for y in range(room.y + 2, room.y + room.h - 2):
			if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
				# Check if it's a floor tile
				if grid[x][y] == 1: # Floor
					var world_x = x * tile_size + tile_size / 2.0
					var world_y = y * tile_size + tile_size / 2.0
					valid_positions.append(Vector2(world_x, world_y))
	
	if valid_positions.size() == 0:
		return enemies
	
	# Shuffle positions to randomize enemy placement
	valid_positions.shuffle()
	
	# Place enemies
	for i in range(min(num_enemies, valid_positions.size())):
		var enemy_type = enemy_types[rng.randi() % enemy_types.size()]
		var position = valid_positions[i]
		
		# Randomize stats (similar to player but weaker)
		# Base stats vary by enemy type
		var max_health = rng.randf_range(18.0, 35.0) # Reduced from 30.0-60.0 for better balance
		var move_speed: float
		var damage = rng.randf_range(5.0, 15.0)
		
		# Set appropriate move speed based on enemy type
		if enemy_type.ends_with("bat.tscn"):
			move_speed = rng.randf_range(35.0, 45.0) # Bats: slower
		elif enemy_type.ends_with("slime.tscn"):
			move_speed = rng.randf_range(18.0, 25.0) # Slimes: very slow (reduced from 30-40)
		elif enemy_type.ends_with("rat.tscn"):
			move_speed = rng.randf_range(40.0, 50.0) # Rats: slow
		elif enemy_type.ends_with("hand.tscn"):
			move_speed = rng.randf_range(25.0, 32.0) # Hands: slow (reduced from 55.0 to 28.0 in script)
		else:
			move_speed = rng.randf_range(50.0, 80.0) # Other enemies (humanoids): faster
		
		var enemy_data = {
			"type": enemy_type,
			"position": position,
			"room": room, # Store reference to room for AI
			"max_health": max_health,
			"move_speed": move_speed,
			"damage": damage
		}
		
		# If it's a humanoid enemy, randomize the humanoid_type
		if enemy_type.ends_with("enemy_humanoid.tscn"):
			# Random humanoid type: 0=CYCLOPS, 1=DEMON, 2=HUMANOID, 3=NIGHTELF, 4=GOBLIN, 5=ORC, 6=SKELETON
			# Weight towards common types (goblins, humans, orcs) - 40% goblin, 30% humanoid, 20% orc, 10% other
			var rand_val = rng.randf()
			var humanoid_type = 2 # Default to HUMANOID
			if rand_val < 0.4:
				humanoid_type = 4 # GOBLIN (40%)
			elif rand_val < 0.7:
				humanoid_type = 2 # HUMANOID (30%)
			elif rand_val < 0.9:
				humanoid_type = 5 # ORC (20%)
			else:
				# 10% for other types (distributed evenly)
				var other_types = [0, 1, 3, 6] # CYCLOPS, DEMON, NIGHTELF, SKELETON
				humanoid_type = other_types[rng.randi() % other_types.size()]
			enemy_data["humanoid_type"] = humanoid_type
		
		enemies.append(enemy_data)
	
	return enemies

func _place_stairs_in_exit_room(exit_room: Dictionary, grid: Array, tile_grid: Array, map_size: Vector2i, all_doors: Array, rng: RandomNumberGenerator) -> Dictionary:
	# Place stairs in the exit room on one of the walls
	# Stairs are rendered like doors but with different middle frame tile
	# Choose a random wall to place stairs on (excluding corners)
	# Make sure stairs don't overlap any doors
	# Returns stairs data with position and size for Area2D creation
	LogManager.log("DungeonGenerator: Placing stairs in exit room: " + str(exit_room.x) + "," + str(exit_room.y) + " size: " + str(exit_room.w) + "x" + str(exit_room.h) + " doors: " + str(all_doors.size()), LogManager.CATEGORY_DUNGEON)
	
	var stairs_data: Dictionary = {}
	var wall_choices = []
	var tile_size = 16
	
	# Helper function to check if stairs position overlaps with any door
	# This checks if ANY tile of the stairs overlaps ANY tile of any door
	# Doors are either 3x2 (vertical: N/S) or 2x3 (horizontal: E/W)
	# CRITICAL: Must check ALL door tiles, not just door position!
	# Uses both door dictionary AND grid check for reliability
	var stairs_overlaps_door = func(stairs_x: int, stairs_y: int, stairs_w: int, stairs_h: int) -> bool:
		# FIRST: Check grid - if any stairs tile is marked as a door (grid value 2), it's an overlap
		for stairs_tile_x in range(stairs_x, stairs_x + stairs_w):
			for stairs_tile_y in range(stairs_y, stairs_y + stairs_h):
				if stairs_tile_x >= 0 and stairs_tile_x < map_size.x and stairs_tile_y >= 0 and stairs_tile_y < map_size.y:
					if grid[stairs_tile_x][stairs_tile_y] == 2: # Grid value 2 = door
						LogManager.log("DungeonGenerator: Stairs tile (" + str(stairs_tile_x) + "," + str(stairs_tile_y) + ") is marked as door in grid!", LogManager.CATEGORY_DUNGEON)
						return true
		
		# SECOND: Check door dictionary - verify against all known doors
		for stairs_tile_x in range(stairs_x, stairs_x + stairs_w):
			for stairs_tile_y in range(stairs_y, stairs_y + stairs_h):
				# Check this stairs tile against all door tiles from door dictionary
				for door in all_doors:
					var door_x = door.x
					var door_y = door.y
					
					# Determine actual door dimensions and tile positions based on direction
					# CRITICAL: Door x,y is the START position, but door spans multiple tiles!
					var door_tiles: Array = [] # Array of {x, y} for each door tile
					
					if "dir" in door:
						match door.dir:
							"E", "W":
								# Horizontal doors (E/W): 2x3 tiles (2 wide, 3 tall)
								# Door starts at (door_x, door_y) and spans 2x3
								for door_dx in range(2):
									for door_dy in range(3):
										door_tiles.append({"x": door_x + door_dx, "y": door_y + door_dy})
							"N", "S":
								# Vertical doors (N/S): 3x2 tiles (3 wide, 2 tall)
								# Door starts at (door_x, door_y) and spans 3x2
								for door_dx in range(3):
									for door_dy in range(2):
										door_tiles.append({"x": door_x + door_dx, "y": door_y + door_dy})
							_:
								# Fallback: assume 3x2 (vertical)
								for door_dx in range(3):
									for door_dy in range(2):
										door_tiles.append({"x": door_x + door_dx, "y": door_y + door_dy})
					else:
						# Fallback if no direction: assume 3x2 (vertical)
						for door_dx in range(3):
							for door_dy in range(2):
								door_tiles.append({"x": door_x + door_dx, "y": door_y + door_dy})
					
					# Check if this stairs tile matches any door tile
					for door_tile in door_tiles:
						if stairs_tile_x == door_tile.x and stairs_tile_y == door_tile.y:
							var door_dir_str = str(door.dir) if "dir" in door else "unknown"
							LogManager.log("DungeonGenerator: Stairs tile (" + str(stairs_tile_x) + "," + str(stairs_tile_y) + ") overlaps door tile (" + str(door_tile.x) + "," + str(door_tile.y) + ") from door at (" + str(door_x) + "," + str(door_y) + ") dir: " + door_dir_str, LogManager.CATEGORY_DUNGEON)
							return true
		return false
	
	# Determine which walls are available (not blocked by doors)
	# Use same logic as doors: at least 2 tiles from corners, 5 tiles from opposite edge
	# If room is exactly 3 tiles wide/tall, stairs must be exactly in the middle
	
	# Top wall - stairs are 3 tiles wide, need at least 2 tiles from corners (same as doors)
	# Minimum room width: 2 (left buffer) + 3 (stairs) + 2 (right buffer) = 7 tiles
	if exit_room.w >= 7: # Minimum room width for 3-tile stairs with corner buffers
		var min_x = exit_room.x + 2 # At least 2 tiles from left corner
		var max_x = exit_room.x + exit_room.w - 5 # At least 5 tiles from right edge (2 buffer + 3 stairs)
		
		if max_x >= min_x:
			wall_choices.append({
				"dir": "UP",
				"x_range": range(min_x, max_x + 1), # +1 because range is exclusive
				"y": exit_room.y,
				"tile_start": STAIRS_UP_START,
				"w": 3,
				"h": 2
			})
	
	# Bottom wall - stairs are 3 tiles wide, need at least 2 tiles from corners (same as doors)
	# Minimum room width: 2 (left buffer) + 3 (stairs) + 2 (right buffer) = 7 tiles
	# Use same logic as doors: at least 2 tiles from left corner, 5 tiles from right edge
	if exit_room.w >= 7:
		var min_x = exit_room.x + 2 # At least 2 tiles from left corner (same as doors)
		var max_x = exit_room.x + exit_room.w - 5 # At least 5 tiles from right edge (2 buffer + 3 stairs)
		
		if max_x >= min_x:
			# Bottom wall: door is at exit_room.y + exit_room.h - 2 (upper tile) and exit_room.y + exit_room.h - 1 (lower tile)
			# Stairs should start at the same position as doors
			wall_choices.append({
				"dir": "DOWN",
				"x_range": range(min_x, max_x + 1),
				"y": exit_room.y + exit_room.h - 2, # Start at upper wall tile (same as doors)
				"tile_start": STAIRS_DOWN_START,
				"w": 3,
				"h": 2
			})
	
	# Left wall - stairs are 3 tiles tall, need at least 2 tiles from corners
	# Minimum room height: 2 (top buffer) + 3 (stairs) + 2 (bottom buffer) = 7 tiles
	if exit_room.h >= 7: # Minimum room height for 3-tile stairs with corner buffers
		var min_y = exit_room.y + 2 # At least 2 tiles from top corner
		var max_y = exit_room.y + exit_room.h - 5 # At least 5 tiles from bottom edge (2 buffer + 3 stairs)
		
		if max_y >= min_y:
			wall_choices.append({
				"dir": "LEFT",
				"y_range": range(min_y, max_y + 1),
				"x": exit_room.x,
				"tile_start": STAIRS_LEFT_START,
				"w": 2,
				"h": 3
			})
	
	# Right wall - stairs are 3 tiles tall, need at least 2 tiles from corners
	# Minimum room height: 2 (top buffer) + 3 (stairs) + 2 (bottom buffer) = 7 tiles
	if exit_room.h >= 7:
		var min_y = exit_room.y + 2 # At least 2 tiles from top corner
		var max_y = exit_room.y + exit_room.h - 5 # At least 5 tiles from bottom edge (2 buffer + 3 stairs)
		
		if max_y >= min_y:
			wall_choices.append({
				"dir": "RIGHT",
				"y_range": range(min_y, max_y + 1),
				"x": exit_room.x + exit_room.w - 2, # Right wall is 2 tiles wide
				"tile_start": STAIRS_RIGHT_START,
				"w": 2,
				"h": 3
			})
	
	if wall_choices.size() == 0:
		LogManager.log_error("DungeonGenerator: ERROR - No valid walls for stairs! Exit room too small: " + str(exit_room.w) + "x" + str(exit_room.h), LogManager.CATEGORY_DUNGEON)
		return {} # No valid walls for stairs
	
	# Choose a random wall
	var wall = wall_choices[rng.randi() % wall_choices.size()]
	
	if wall.dir == "UP" or wall.dir == "DOWN":
		# Horizontal stairs (3x2)
		if wall.x_range.size() == 0:
			LogManager.log_error("DungeonGenerator: ERROR - x_range is empty for " + str(wall.dir) + " stairs", LogManager.CATEGORY_DUNGEON)
			return {}
		
		# Try to find a position that doesn't overlap doors
		var valid_positions = []
		for test_x in wall.x_range:
			var test_stairs_start_x = test_x - 1 # Start 1 tile to the left (3 tiles wide)
			if not stairs_overlaps_door.call(test_stairs_start_x, wall.y, wall.w, wall.h):
				valid_positions.append(test_x)
		
		if valid_positions.size() == 0:
			LogManager.log_error("DungeonGenerator: ERROR - No valid position found for " + str(wall.dir) + " stairs (all positions overlap doors)", LogManager.CATEGORY_DUNGEON)
			# Don't allow stairs to overlap doors - this is a critical bug
			# Instead, try the next wall or return empty to force placement elsewhere
			return {} # No valid position found - will trigger _force_place_stairs
		
		# Choose random valid position
		var stairs_x = valid_positions[rng.randi() % valid_positions.size()]
		var stairs_start_x = stairs_x - 1 # Start 1 tile to the left (3 tiles wide)
		
		# Store stairs data for Area2D creation
		stairs_data = {
			"x": stairs_start_x,
			"y": wall.y,
			"w": wall.w,
			"h": wall.h,
			"dir": wall.dir,
					"world_pos": Vector2((stairs_start_x + wall.w / 2.0) * tile_size, (wall.y + wall.h / 2.0) * tile_size),
			"world_size": Vector2(wall.w * tile_size, wall.h * tile_size)
		}
		
		LogManager.log("DungeonGenerator: Placed " + str(wall.dir) + " stairs at tile (" + str(stairs_start_x) + "," + str(wall.y) + ") world pos: " + str(stairs_data.world_pos) + " in room (" + str(exit_room.x) + "," + str(exit_room.y) + ") size " + str(exit_room.w) + "x" + str(exit_room.h), LogManager.CATEGORY_DUNGEON)
		
		# Mark grid cells as stairs (similar to doors)
		for dx in range(wall.w):
			for dy in range(wall.h):
				var x = stairs_start_x + dx
				var y = wall.y + dy
				if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
					grid[x][y] = 4 # Stairs (use grid value 4)
					# Render stairs tiles (similar to doors but with different middle frame)
					if wall.dir == "UP":
						if dy == 0: # First row
							if dx == 1: # Middle tile
								tile_grid[x][y] = Vector2i(10, 0) # Special middle tile for UP stairs
							else:
								tile_grid[x][y] = wall.tile_start + Vector2i(dx, dy)
						else:
							tile_grid[x][y] = wall.tile_start + Vector2i(dx, dy)
					else: # DOWN
						# For DOWN stairs, use same tiles as door DOWN
						# Row 0 (dy=0): use door tiles
						# Row 1 (dy=1): use door tiles, except middle tile (col 1, row 1) which is 6,6
						if dy == 1 and dx == 1: # Second row, middle column (col 1, row 1)
							tile_grid[x][y] = Vector2i(6, 6) # Special middle tile for DOWN stairs
						else:
							# Use door DOWN tiles (same as DOOR_BOTTOM_START = 7,5)
							tile_grid[x][y] = wall.tile_start + Vector2i(dx, dy)
	
	elif wall.dir == "LEFT" or wall.dir == "RIGHT":
		# Vertical stairs (2x3)
		if wall.y_range.size() == 0:
			LogManager.log_error("DungeonGenerator: ERROR - y_range is empty for " + str(wall.dir) + " stairs", LogManager.CATEGORY_DUNGEON)
			return {}
		
		# Try to find a position that doesn't overlap doors
		var valid_positions = []
		for test_y in wall.y_range:
			var test_stairs_start_y = test_y - 1 # Start 1 tile up (3 tiles tall)
			if not stairs_overlaps_door.call(wall.x, test_stairs_start_y, wall.w, wall.h):
				valid_positions.append(test_y)
		
		if valid_positions.size() == 0:
			LogManager.log_error("DungeonGenerator: ERROR - No valid position found for " + str(wall.dir) + " stairs (all positions overlap doors)", LogManager.CATEGORY_DUNGEON)
			# Don't allow stairs to overlap doors - this is a critical bug
			# Instead, try the next wall or return empty to force placement elsewhere
			return {} # No valid position found - will trigger _force_place_stairs
		
		# Choose random valid position
		var stairs_y = valid_positions[rng.randi() % valid_positions.size()]
		var stairs_start_y = stairs_y - 1 # Start 1 tile up (3 tiles tall)
		
		# Store stairs data for Area2D creation
		stairs_data = {
			"x": wall.x,
			"y": stairs_start_y,
			"w": wall.w,
			"h": wall.h,
			"dir": wall.dir,
			"world_pos": Vector2((wall.x + wall.w / 2.0) * tile_size, (stairs_start_y + wall.h / 2.0) * tile_size),
			"world_size": Vector2(wall.w * tile_size, wall.h * tile_size)
		}
		
		LogManager.log("DungeonGenerator: Placed " + str(wall.dir) + " stairs at tile (" + str(wall.x) + "," + str(stairs_start_y) + ") world pos: " + str(stairs_data.world_pos) + " in room (" + str(exit_room.x) + "," + str(exit_room.y) + ") size " + str(exit_room.w) + "x" + str(exit_room.h), LogManager.CATEGORY_DUNGEON)
		
		# Mark grid cells as stairs
		for dx in range(wall.w):
			for dy in range(wall.h):
				var x = wall.x + dx
				var y = stairs_start_y + dy
				if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
					grid[x][y] = 4 # Stairs
					# Render stairs tiles with special middle frame
					if wall.dir == "LEFT":
						if dx == 0: # First column
							if dy == 1: # Middle tile (second row)
								tile_grid[x][y] = Vector2i(5, 1) # Special middle tile for LEFT stairs
							else:
								tile_grid[x][y] = wall.tile_start + Vector2i(dx, dy)
						else:
							tile_grid[x][y] = wall.tile_start + Vector2i(dx, dy)
					else: # RIGHT
						if dx == 1: # Second column
							if dy == 1: # Middle tile (second row)
								tile_grid[x][y] = Vector2i(11, 1) # Special middle tile for RIGHT stairs
							else:
								tile_grid[x][y] = wall.tile_start + Vector2i(dx, dy)
						else:
							tile_grid[x][y] = wall.tile_start + Vector2i(dx, dy)
	
	return stairs_data

func _place_entrance_in_start_room(start_room: Dictionary, grid: Array, tile_grid: Array, map_size: Vector2i, all_doors: Array, rng: RandomNumberGenerator) -> Dictionary:
	# Place entrance in the start room (similar to stairs but for entrance)
	# Entrance is rendered like stairs but with different purpose
	# Returns entrance data with position and size for Area2D creation
	LogManager.log("DungeonGenerator: Placing entrance in start room: " + str(start_room.x) + "," + str(start_room.y) + " size: " + str(start_room.w) + "x" + str(start_room.h) + " doors: " + str(all_doors.size()), LogManager.CATEGORY_DUNGEON)
	
	# Reuse the stairs placement logic (entrance looks like stairs)
	return _place_stairs_in_exit_room(start_room, grid, tile_grid, map_size, all_doors, rng)

func _force_place_stairs(exit_room: Dictionary, grid: Array, tile_grid: Array, map_size: Vector2i, all_doors: Array, _rng: RandomNumberGenerator) -> Dictionary:
	# Force place stairs in exit room - used as fallback when normal placement fails
	# Still tries to avoid door overlaps, but will place stairs even if room is small
	# Uses same positioning logic as doors: at least 2 tiles from corners
	LogManager.log("DungeonGenerator: Force placing stairs in exit room: " + str(exit_room.x) + "," + str(exit_room.y) + " size: " + str(exit_room.w) + "x" + str(exit_room.h), LogManager.CATEGORY_DUNGEON)
	
	var stairs_data: Dictionary = {}
	var tile_size = 16
	
	# Helper function to check if stairs position overlaps with any door (same as in _place_stairs_in_exit_room)
	# CRITICAL: Check each stairs tile against each door tile to ensure no overlap
	# Uses both door dictionary AND grid check for reliability
	var stairs_overlaps_door = func(test_x: int, test_y: int, test_w: int, test_h: int) -> bool:
		# FIRST: Check grid - if any stairs tile is marked as a door (grid value 2), it's an overlap
		for stairs_tile_x in range(test_x, test_x + test_w):
			for stairs_tile_y in range(test_y, test_y + test_h):
				if stairs_tile_x >= 0 and stairs_tile_x < map_size.x and stairs_tile_y >= 0 and stairs_tile_y < map_size.y:
					if grid[stairs_tile_x][stairs_tile_y] == 2: # Grid value 2 = door
						return true
		
		# SECOND: Check door dictionary - verify against all known doors
		for stairs_tile_x in range(test_x, test_x + test_w):
			for stairs_tile_y in range(test_y, test_y + test_h):
				# Check this stairs tile against all door tiles from door dictionary
				for door in all_doors:
					var door_x = door.x
					var door_y = door.y
					
					# Determine actual door tile positions based on direction
					var door_tiles: Array = [] # Array of {x, y} for each door tile
					
					if "dir" in door:
						match door.dir:
							"E", "W":
								# Horizontal doors (E/W): 2x3 tiles (2 wide, 3 tall)
								for door_dx in range(2):
									for door_dy in range(3):
										door_tiles.append({"x": door_x + door_dx, "y": door_y + door_dy})
							"N", "S":
								# Vertical doors (N/S): 3x2 tiles (3 wide, 2 tall)
								for door_dx in range(3):
									for door_dy in range(2):
										door_tiles.append({"x": door_x + door_dx, "y": door_y + door_dy})
							_:
								# Fallback: assume 3x2 (vertical)
								for door_dx in range(3):
									for door_dy in range(2):
										door_tiles.append({"x": door_x + door_dx, "y": door_y + door_dy})
					else:
						# Fallback: assume 3x2 (vertical)
						for door_dx in range(3):
							for door_dy in range(2):
								door_tiles.append({"x": door_x + door_dx, "y": door_y + door_dy})
					
					# Check if this stairs tile matches any door tile
					for door_tile in door_tiles:
						if stairs_tile_x == door_tile.x and stairs_tile_y == door_tile.y:
							return true
		return false
	
	# Calculate safe position for stairs (3 tiles wide, 2 tiles tall)
	# Try to place on top wall first, then bottom, then left, then right, then center floor
	var stairs_x = 0
	var stairs_y = 0
	var stairs_w = 3
	var stairs_h = 2
	var stairs_dir = "UP"
	var found_position = false
	
	# Try top wall (preferred) - use same logic as doors
	# Minimum room width: 2 (left buffer) + 3 (stairs) + 2 (right buffer) = 7 tiles
	if exit_room.w >= 7:
		var min_x = exit_room.x + 2 # At least 2 tiles from left corner
		var max_x = exit_room.x + exit_room.w - 5 # At least 5 tiles from right edge (2 buffer + 3 stairs)
		# Try multiple positions to avoid doors
		for test_x in range(min_x, max_x + 1):
			var test_stairs_start_x = test_x - 1 # Start 1 tile to the left (3 tiles wide)
			if not stairs_overlaps_door.call(test_stairs_start_x, exit_room.y, stairs_w, stairs_h):
				stairs_x = test_stairs_start_x
				stairs_y = exit_room.y
				stairs_dir = "UP"
				found_position = true
				break
		# Don't place if all positions overlap - try next wall instead
	
	# Try bottom wall
	if not found_position and exit_room.w >= 7:
		var min_x = exit_room.x + 2
		var max_x = exit_room.x + exit_room.w - 5
		for test_x in range(min_x, max_x + 1):
			var test_stairs_start_x = test_x - 1
			if not stairs_overlaps_door.call(test_stairs_start_x, exit_room.y + exit_room.h - stairs_h, stairs_w, stairs_h):
				stairs_x = test_stairs_start_x
				stairs_y = exit_room.y + exit_room.h - stairs_h
				stairs_dir = "DOWN"
				found_position = true
				break
		# Don't place if all positions overlap - try next wall instead
	
	# Try left wall (vertical stairs)
	# Minimum room height: 2 (top buffer) + 3 (stairs) + 2 (bottom buffer) = 7 tiles
	if not found_position and exit_room.h >= 7:
		var min_y = exit_room.y + 2
		var max_y = exit_room.y + exit_room.h - 5
		stairs_w = 2
		stairs_h = 3
		for test_y in range(min_y, max_y + 1):
			var test_stairs_start_y = test_y - 1 # Start 1 tile up (3 tiles tall)
			if not stairs_overlaps_door.call(exit_room.x, test_stairs_start_y, stairs_w, stairs_h):
				stairs_x = exit_room.x
				stairs_y = test_stairs_start_y
				stairs_dir = "LEFT"
				found_position = true
				break
		# Don't place if all positions overlap - try next wall instead
	
	# Try right wall (vertical stairs)
	if not found_position and exit_room.h >= 7:
		var min_y = exit_room.y + 2
		var max_y = exit_room.y + exit_room.h - 5
		stairs_w = 2
		stairs_h = 3
		for test_y in range(min_y, max_y + 1):
			var test_stairs_start_y = test_y - 1
			if not stairs_overlaps_door.call(exit_room.x + exit_room.w - 2, test_stairs_start_y, stairs_w, stairs_h):
				stairs_x = exit_room.x + exit_room.w - 2
				stairs_y = test_stairs_start_y
				stairs_dir = "RIGHT"
				found_position = true
				break
		# Don't place if all positions overlap - try next wall instead
	
	# Last resort: place in center of room floor (any room size)
	# But ONLY if it doesn't overlap any doors!
	if not found_position:
		stairs_w = 3
		stairs_h = 2
		var center_x = exit_room.x + max(1, (exit_room.w - stairs_w) / 2)
		var center_y = exit_room.y + max(1, (exit_room.h - stairs_h) / 2)
		center_x = clamp(center_x, exit_room.x + 1, exit_room.x + exit_room.w - stairs_w - 1)
		center_y = clamp(center_y, exit_room.y + 1, exit_room.y + exit_room.h - stairs_h - 1)
		
		# Check if center position overlaps any doors
		if not stairs_overlaps_door.call(center_x, center_y, stairs_w, stairs_h):
			stairs_x = center_x
			stairs_y = center_y
			stairs_dir = "UP"
			found_position = true
		else:
			# Try to find ANY free position in the room that doesn't overlap doors
			for test_y in range(exit_room.y + 1, exit_room.y + exit_room.h - stairs_h):
				for test_x in range(exit_room.x + 1, exit_room.x + exit_room.w - stairs_w):
					if not stairs_overlaps_door.call(test_x, test_y, stairs_w, stairs_h):
						stairs_x = test_x
						stairs_y = test_y
						stairs_dir = "UP"
						found_position = true
						break
				if found_position:
					break
	
	# If still no valid position found, return empty (don't place stairs that overlap doors!)
	if not found_position:
		LogManager.log_error("DungeonGenerator: ERROR - Could not find any position for stairs that doesn't overlap doors!", LogManager.CATEGORY_DUNGEON)
		return {}
	
	stairs_data = {
		"x": stairs_x,
		"y": stairs_y,
		"w": stairs_w,
		"h": stairs_h,
		"dir": stairs_dir,
		"world_pos": Vector2((stairs_x + stairs_w / 2.0) * tile_size, (stairs_y + stairs_h / 2.0) * tile_size),
		"world_size": Vector2(stairs_w * tile_size, stairs_h * tile_size)
	}
	
	# Mark grid cells as stairs
	for dx in range(stairs_data.w):
		for dy in range(stairs_data.h):
			var x = stairs_data.x + dx
			var y = stairs_data.y + dy
			if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
				grid[x][y] = 4 # Stairs
				# Render stairs tiles based on direction (same as normal placement)
				if stairs_dir == "UP":
					if dy == 0: # First row
						if dx == 1: # Middle tile
							tile_grid[x][y] = Vector2i(10, 0) # Special middle tile for UP stairs
						else:
							tile_grid[x][y] = STAIRS_UP_START + Vector2i(dx, dy)
					else:
						tile_grid[x][y] = STAIRS_UP_START + Vector2i(dx, dy)
				elif stairs_dir == "DOWN":
					# For DOWN stairs, use same tiles as door DOWN
					# Row 0 (dy=0): use door tiles
					# Row 1 (dy=1): use door tiles, except middle tile (col 1, row 1) which is 6,6
					if dy == 1 and dx == 1: # Second row, middle column (col 1, row 1)
						tile_grid[x][y] = Vector2i(6, 6) # Special middle tile for DOWN stairs
					else:
						# Use door DOWN tiles (same as DOOR_BOTTOM_START = STAIRS_DOWN_START = 7,5)
						tile_grid[x][y] = STAIRS_DOWN_START + Vector2i(dx, dy)
				elif stairs_dir == "LEFT":
					# Vertical stairs on left wall
					if dx == 0: # First column
						if dy == 1: # Middle tile (second row)
							tile_grid[x][y] = Vector2i(5, 1) # Special middle tile for LEFT stairs
						else:
							tile_grid[x][y] = STAIRS_LEFT_START + Vector2i(dx, dy)
					else:
						tile_grid[x][y] = STAIRS_LEFT_START + Vector2i(dx, dy)
				elif stairs_dir == "RIGHT":
					# Vertical stairs on right wall
					if dx == 1: # Second column
						if dy == 1: # Middle tile (second row)
							tile_grid[x][y] = Vector2i(11, 1) # Special middle tile for RIGHT stairs
						else:
							tile_grid[x][y] = STAIRS_RIGHT_START + Vector2i(dx, dy)
					else:
						tile_grid[x][y] = STAIRS_RIGHT_START + Vector2i(dx, dy)
				else:
					# Fallback: use UP stairs tiles
					tile_grid[x][y] = STAIRS_UP_START + Vector2i(dx, dy)
	
	LogManager.log("DungeonGenerator: Force placed " + str(stairs_dir) + " stairs at tile (" + str(stairs_data.x) + "," + str(stairs_data.y) + ") world pos: " + str(stairs_data.world_pos), LogManager.CATEGORY_DUNGEON)
	return stairs_data

func _place_interactable_objects_in_room(room: Dictionary, grid: Array, map_size: Vector2i, all_doors: Array, all_enemies: Array, rng: RandomNumberGenerator, room_puzzle_data: Dictionary = {}) -> Array:
	# Place interactable objects in a room
	# Small rooms (7-8 tiles): 0-1 objects
	# Medium rooms (9-10 tiles): 0-3 objects
	# Large rooms (11-12 tiles): 0-8 objects
	# Returns array of interactable object data dictionaries
	# CRITICAL: If room has "switch_pillar" puzzle, MUST spawn at least 1 Pillar (regardless of room size)
	var objects = []
	var tile_size = 16
	
	# Check if room has a "switch_pillar" puzzle - if so, we MUST spawn at least 1 pillar
	var has_pillar_switch_puzzle = false
	var matching_puzzle_room = null
	if room_puzzle_data.size() > 0:
		# Try direct lookup first (room dictionary as key)
		if room_puzzle_data.has(room):
			matching_puzzle_room = room
		else:
			# Fallback: find matching room by comparing values (x, y, w, h)
			for puzzle_room in room_puzzle_data.keys():
				# Compare rooms by values (x, y, w, h)
				if puzzle_room.x == room.x and puzzle_room.y == room.y and \
				   puzzle_room.w == room.w and puzzle_room.h == room.h:
					matching_puzzle_room = puzzle_room
					break
		
		if matching_puzzle_room != null:
			var puzzle_info = room_puzzle_data[matching_puzzle_room]
			LogManager.log("DungeonGenerator: Checking room (" + str(room.x) + "," + str(room.y) + ") - puzzle_room (" + str(matching_puzzle_room.x) + "," + str(matching_puzzle_room.y) + ") puzzle_type: " + str(puzzle_info.type), LogManager.CATEGORY_DUNGEON)
			if puzzle_info.type == "switch_pillar":
				has_pillar_switch_puzzle = true
				LogManager.log("DungeonGenerator: Room (" + str(room.x) + "," + str(room.y) + ") has pillar switch puzzle - will spawn at least 1 pillar", LogManager.CATEGORY_DUNGEON)
	else:
		LogManager.log("DungeonGenerator: room_puzzle_data is empty for room (" + str(room.x) + "," + str(room.y) + ")", LogManager.CATEGORY_DUNGEON)
	
	# Calculate room floor area (excluding walls)
	var floor_w = room.w - 4 # Excluding 2-tile walls on each side
	var floor_h = room.h - 4
	var floor_area = floor_w * floor_h
	
	# Determine max objects based on room size
	var max_objects: int = 0
	if floor_area <= 16: # Small rooms (4x4 or smaller floor)
		max_objects = 1
	elif floor_area <= 36: # Medium rooms (up to 6x6 floor)
		max_objects = 3
	else: # Large rooms (7x7+ floor)
		max_objects = 8
	
	var num_objects = rng.randi_range(0, max_objects)
	
	# CRITICAL: If room has pillar switch puzzle, ensure we spawn at least 1 pillar
	# This MUST happen regardless of room size
	if has_pillar_switch_puzzle:
		# Set minimum to 1 if num_objects is 0
		if num_objects == 0:
			num_objects = 1
		# The pillar will be placed FIRST in the objects list (before any other objects)
	
	# Available object types and their setup functions
	var object_types = [
		{"type": "Pot", "setup": "setup_pot"},
		{"type": "LiftableBarrel", "setup": "setup_liftable_barrel"},
		{"type": "PushableBarrel", "setup": "setup_pushable_barrel"},
		{"type": "Box", "setup": "setup_box"},
		{"type": "Chest", "setup": "setup_chest"},
		{"type": "Pillar", "setup": "setup_pillar"},
		{"type": "PushableHighBox", "setup": "setup_pushable_high_box"}
	]
	
	# Find valid floor positions in the room (excluding walls, enemies, and door areas)
	var valid_positions = []
	
	# Room interior is from room.x + 2 to room.x + room.w - 2 (excluding 2-tile walls)
	# CRITICAL: Also exclude last row/column of floor tiles to prevent objects from spawning in walls
	# Objects are 16x16, so we need at least 1 tile buffer from walls
	# Floor tiles are at: room.x+2, room.x+3, ..., room.x+room.w-3 (last floor tile before wall)
	# To exclude last tile, we want: room.x+2, room.x+3, ..., room.x+room.w-4
	var min_x = room.x + 2
	var max_x = room.x + room.w - 4 # Exclude last 2 floor tiles (room.w-3 is last floor, room.w-4 is second-to-last)
	var min_y = room.y + 2
	var max_y = room.y + room.h - 4 # Exclude last 2 floor tiles (room.h-3 is last floor, room.h-4 is second-to-last)
	
	for x in range(min_x, max_x + 1): # +1 because range is exclusive at end
		for y in range(min_y, max_y + 1):
			if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
				# Check if it's a floor tile
				if grid[x][y] == 1: # Floor
					# CRITICAL: Interactable objects are 16x16 pixels with origin at (0,0) (top-left)
					# To center a 16x16 sprite in a 16x16 tile, we need to offset by 8 pixels into the tile
					# Tile (x,y) spans from (x*16, y*16) to ((x+1)*16, (y+1)*16)
					# Position sprite 8 pixels into the tile from top-left: (x*16 + 8, y*16 + 8)
					var world_x = x * tile_size + 8
					var world_y = y * tile_size + 8
					var world_pos = Vector2(world_x, world_y)
					
					# Check if position is valid (not blocked by door, not occupied by enemy)
					if _is_valid_interactable_position(world_pos, all_doors, all_enemies, room):
						valid_positions.append(world_pos)
	
	# Early return if no valid positions (unless pillar is required, but that's handled below)
	if valid_positions.size() == 0:
		if has_pillar_switch_puzzle:
			# CRITICAL: Pillar is REQUIRED, so we must find at least one position
			# Try a more permissive search - use ALL floor tiles in the room (even if near doors/walls)
			LogManager.log("DungeonGenerator: Room (" + str(room.x) + "," + str(room.y) + ") has pillar switch puzzle but no valid positions. Trying fallback search...", LogManager.CATEGORY_DUNGEON)
			# Use same bounds but skip the position validation check
			var found_fallback = false
			for x in range(min_x, max_x + 1):
				for y in range(min_y, max_y + 1):
					if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
						if grid[x][y] == 1: # Floor
							var world_x = x * tile_size + 8
							var world_y = y * tile_size + 8
							var world_pos = Vector2(world_x, world_y)
							valid_positions.append(world_pos)
							# Only need one position for the required pillar
							found_fallback = true
							break
				if found_fallback:
					break
			
			if valid_positions.size() == 0:
				push_warning("DungeonGenerator: Room (", room.x, ",", room.y, ") has pillar switch puzzle but NO floor tiles! Cannot place pillar.")
				return objects
			else:
				LogManager.log("DungeonGenerator: Found fallback position for required pillar in room (" + str(room.x) + "," + str(room.y) + ")", LogManager.CATEGORY_DUNGEON)
		else:
			# No pillar required, safe to return
			return objects
	
	# Shuffle positions to randomize placement
	valid_positions.shuffle()
	
	# CRITICAL: If room has pillar switch puzzle, the FIRST object MUST be a Pillar
	if has_pillar_switch_puzzle:
		# Place pillar as the first object
		var pillar_type_data = {"type": "Pillar", "setup": "setup_pillar"}
		objects.append({
			"type": pillar_type_data.type,
			"setup_function": pillar_type_data.setup,
			"position": valid_positions[0],
			"room": room
		})
		
		# Place remaining objects (skip first position since it's used by the pillar)
		# We need to place num_objects - 1 more objects (since pillar counts as 1)
		var remaining_objects = num_objects - 1
		var positions_index = 1 # Start from second position
		for i in range(min(remaining_objects, valid_positions.size() - 1)):
			if positions_index >= valid_positions.size():
				break # No more valid positions
			var object_type_data = object_types[rng.randi() % object_types.size()]
			# Skip Pillar type for remaining objects (already placed one)
			while object_type_data.type == "Pillar":
				object_type_data = object_types[rng.randi() % object_types.size()]
			
			objects.append({
				"type": object_type_data.type,
				"setup_function": object_type_data.setup,
				"position": valid_positions[positions_index],
				"room": room
			})
			positions_index += 1
	else:
		# Normal placement: no pillar requirement
		for i in range(min(num_objects, valid_positions.size())):
			var object_type_data = object_types[rng.randi() % object_types.size()]
			var position = valid_positions[i]
			
			objects.append({
				"type": object_type_data.type,
				"setup_function": object_type_data.setup,
				"position": position,
				"room": room
			})
	
	return objects

func _is_valid_interactable_position(world_pos: Vector2, all_doors: Array, all_enemies: Array, room: Dictionary) -> bool:
	# Check if position is not blocked by a door or occupied by an enemy
	var tile_size = 16
	var tile_x = int(world_pos.x / tile_size)
	var tile_y = int(world_pos.y / tile_size)
	
	# Check if position is in front of a door (within 2 tiles of door center)
	for door in all_doors:
		var door_x = door.x
		var door_y = door.y
		
		# Determine actual door dimensions based on direction
		var door_w: int
		var door_h: int
		if "dir" in door:
			match door.dir:
				"E", "W":
					# Horizontal doors: 2x3
					door_w = 2
					door_h = 3
				"N", "S":
					# Vertical doors: 3x2
					door_w = 3
					door_h = 2
				_:
					door_w = door.w if "w" in door else 3
					door_h = door.h if "h" in door else 2
		else:
			door_w = 3
			door_h = 2
		
		# Check if position is within door area
		if tile_x >= door_x and tile_x < door_x + door_w and \
		   tile_y >= door_y and tile_y < door_y + door_h:
			return false
		
		# Check if position is within 2 tiles in front of door (based on door direction)
		# Check if door connects to this room
		var door_connects_to_room = false
		if "room1" in door and door.room1 == room:
			door_connects_to_room = true
		elif "room2" in door and door.room2 == room:
			door_connects_to_room = true
		
		if door_connects_to_room:
			# Check if position is within 2 tiles in front of door
			if door.dir == "E" or door.dir == "W":
				# Horizontal door
				if door.dir == "E":
					# Door on right wall, position should be to the left (inside room)
					if tile_x >= door_x - 2 and tile_x < door_x and \
					   tile_y >= door_y - 1 and tile_y < door_y + door_h + 1:
						return false
				else: # W
					# Door on left wall, position should be to the right (inside room)
					if tile_x > door_x + door_w and tile_x <= door_x + door_w + 2 and \
					   tile_y >= door_y - 1 and tile_y < door_y + door_h + 1:
						return false
			else: # N or S
				# Vertical door
				if door.dir == "S":
					# Door on bottom wall, position should be above (inside room)
					if tile_y >= door_y - 2 and tile_y < door_y and \
					   tile_x >= door_x - 1 and tile_x < door_x + door_w + 1:
						return false
				else: # N
					# Door on top wall, position should be below (inside room)
					if tile_y > door_y + door_h and tile_y <= door_y + door_h + 2 and \
					   tile_x >= door_x - 1 and tile_x < door_x + door_w + 1:
						return false
	
	# Check if position is occupied by an enemy
	for enemy in all_enemies:
		if enemy.has("position") and enemy.position is Vector2:
			var enemy_tile_x = int(enemy.position.x / tile_size)
			var enemy_tile_y = int(enemy.position.y / tile_size)
			# Check if within 1 tile (enemies and objects shouldn't overlap)
			if abs(tile_x - enemy_tile_x) <= 1 and abs(tile_y - enemy_tile_y) <= 1:
				return false
	
	return true

func _find_rooms_before_door(door: Dictionary, start_room: Dictionary, _all_rooms: Array, all_doors: Array, other_blocking_doors: Array = []) -> Array:
	# Find rooms that are reachable from start WITHOUT going through this door
	# This is used to place keys before KeyDoors
	# other_blocking_doors: Array of other doors that should also be treated as blocking (e.g., other key doors)
	var rooms_before_door = []
	var visited = [start_room]
	var queue = [start_room]
	
	# Create a set of all blocking doors (this door + other blocking doors)
	var blocking_doors_set = [door]
	for blocking_door in other_blocking_doors:
		if blocking_door != door:
			blocking_doors_set.append(blocking_door)
	
	while queue.size() > 0:
		var current = queue.pop_front()
		
		# Add current room to result (it's reachable before the door)
		# Don't include the rooms the door connects (need key before reaching them)
		if current != door.room1 and current != door.room2:
			if not rooms_before_door.has(current):
				rooms_before_door.append(current)
		
		# Check all doors connected to current room (except blocking doors)
		for d in all_doors:
			# Skip if this door is in the blocking set
			var is_blocking = false
			for blocking_door in blocking_doors_set:
				if d == blocking_door:
					is_blocking = true
					break
			if is_blocking:
				continue # Skip blocking doors
			
			var next_room = null
			if d.room1 == current:
				next_room = d.room2
			elif d.room2 == current:
				next_room = d.room1
			
			if next_room != null and not visited.has(next_room):
				visited.append(next_room)
				queue.append(next_room)
	
	return rooms_before_door

func _place_blocking_doors(all_rooms: Array, all_doors: Array, grid: Array, map_size: Vector2i, rng: RandomNumberGenerator, start_room_index: int, exit_room_index: int) -> Dictionary:
	# Place blocking doors on existing tile doors
	# Returns array of blocking door data dictionaries
	var blocking_doors = []
	var tile_size = 16
	
	# Get start and exit room references
	var start_room = all_rooms[start_room_index]
	var _exit_room = all_rooms[exit_room_index]
	
	# Calculate reachability from start room to determine where keys can be placed
	var _reachable_rooms_from_start = _find_reachable_rooms(start_room, all_rooms, all_doors)
	
	# Track which rooms have puzzles and which doors are already assigned
	var _rooms_with_puzzles = {} # room -> true
	var assigned_doors = [] # Doors already assigned to a room puzzle
	var room_puzzle_data = {} # room -> {type: "switch" or "enemy", doors: []}
	
	# STEP 1: For each room (except start/exit), randomly decide if it has a door-puzzle
	var puzzle_room_chance = 0.4 # 40% chance per room
	LogManager.log("DungeonGenerator: Assigning puzzles to rooms (" + str(all_rooms.size()) + " total rooms, excluding start/exit)", LogManager.CATEGORY_DUNGEON)
	for i in range(all_rooms.size()):
		if i == start_room_index or i == exit_room_index:
			continue # Skip start and exit rooms
		
		var room = all_rooms[i]
		
		if rng.randf() < puzzle_room_chance:
			LogManager.log("DungeonGenerator: Room (" + str(room.x) + ", " + str(room.y) + ") selected for puzzle assignment", LogManager.CATEGORY_DUNGEON)
			# This room has a puzzle!
			# CRITICAL SAFETY CHECK: Never assign puzzles to start or exit rooms
			# Double-check even though we skip them in the loop
			if i == start_room_index or i == exit_room_index:
				continue
			
			# Find all doors that are connected to this puzzle room
			var doors_in_room = []
			for door in all_doors:
				var door_room1 = door.room1 if ("room1" in door and door.room1 and not door.room1.is_empty()) else null
				var door_room2 = door.room2 if ("room2" in door and door.room2 and not door.room2.is_empty()) else null
				
				var room_matches = false
				
				# Check if room1 matches puzzle room
				if door_room1:
					room_matches = (door_room1.x == room.x and door_room1.y == room.y and \
								   door_room1.w == room.w and door_room1.h == room.h)
				
				# Check if room2 matches puzzle room
				if not room_matches and door_room2:
					room_matches = (door_room2.x == room.x and door_room2.y == room.y and \
								   door_room2.w == room.w and door_room2.h == room.h)
				
				# Door is connected to puzzle room
				if room_matches:
					doors_in_room.append(door)
			
			if doors_in_room.size() == 0:
				LogManager.log("DungeonGenerator: Room (" + str(room.x) + ", " + str(room.y) + ") has no doors connected - skipping puzzle assignment", LogManager.CATEGORY_DUNGEON)
				continue # No doors connected to this room, skip
			
			LogManager.log("DungeonGenerator: Room (" + str(room.x) + ", " + str(room.y) + ") has " + str(doors_in_room.size()) + " doors - assigning puzzle", LogManager.CATEGORY_DUNGEON)
			
			# Decide puzzle type: 33% walk switch, 33% pillar switch, 33% enemy spawner (if room is large enough)
			var can_have_enemies = false
			var interior_width = room.w - 4 # Exclude 2-tile walls
			var interior_height = room.h - 4
			can_have_enemies = (interior_width >= 5 and interior_height >= 5)
			
			var puzzle_type = ""
			var rand_val = rng.randf()
			if can_have_enemies and rand_val < 0.33:
				puzzle_type = "enemy"
			elif rand_val < 0.66:
				puzzle_type = "switch_walk"
			else:
				puzzle_type = "switch_pillar"
			
			# Store puzzle data for this room
			room_puzzle_data[room] = {
				"type": puzzle_type,
				"doors": doors_in_room
			}
			LogManager.log("DungeonGenerator: Stored puzzle data for room (" + str(room.x) + ", " + str(room.y) + ") - type: " + str(puzzle_type) + ", doors: " + str(doors_in_room.size()), LogManager.CATEGORY_DUNGEON)
			
			# Mark these doors as assigned
			for door in doors_in_room:
				assigned_doors.append(door)
	
	LogManager.log("DungeonGenerator: Assigned puzzles to " + str(room_puzzle_data.size()) + " rooms", LogManager.CATEGORY_DUNGEON)
	
	# STEP 2: Create blocking doors for rooms with puzzles
	# CRITICAL: Blocking doors should ONLY be placed ON THE DOORS IN THE PUZZLE ROOM
	# NEVER create blocking doors for rooms that are NOT in room_puzzle_data!
	# When you enter the puzzle room, these doors close, trapping you until you solve the puzzle
	for room in room_puzzle_data.keys():
		# CRITICAL SAFETY CHECK #1: Verify this room is actually in room_puzzle_data
		if not room in room_puzzle_data:
			LogManager.log_error("DungeonGenerator: ERROR - Room (" + str(room.x) + ", " + str(room.y) + ") is NOT in room_puzzle_data! This should never happen!", LogManager.CATEGORY_DUNGEON)
			continue
		
		# CRITICAL SAFETY CHECK #2: Never create blocking doors for start or exit rooms
		var room_index = -1
		for j in range(all_rooms.size()):
			var check_room = all_rooms[j]
			if check_room.x == room.x and check_room.y == room.y and \
			   check_room.w == room.w and check_room.h == room.h:
				room_index = j
				break
		
		if room_index == start_room_index or room_index == exit_room_index:
			LogManager.log_error("DungeonGenerator: ERROR - Attempted to create blocking doors for start/exit room! Skipping.", LogManager.CATEGORY_DUNGEON)
			continue
		
		# CRITICAL SAFETY CHECK #3: Verify this room is actually in all_rooms (sanity check)
		if room_index == -1:
			LogManager.log_error("DungeonGenerator: ERROR - Room (" + str(room.x) + ", " + str(room.y) + ") not found in all_rooms! Skipping.", LogManager.CATEGORY_DUNGEON)
			continue
		
		var puzzle_info = room_puzzle_data[room]
		var doors_in_room = puzzle_info.doors # Doors that are IN this puzzle room (lead OUT OF it)
		var puzzle_type = puzzle_info.type
		
		if doors_in_room.size() == 0:
			LogManager.log("DungeonGenerator: WARNING - Room has puzzle but no doors! Skipping.", LogManager.CATEGORY_DUNGEON)
			continue
		
		# Randomly choose door type: 50% StoneDoor, 50% GateDoor
		var door_type = "StoneDoor" if rng.randf() < 0.5 else "GateDoor"
		
		# Create puzzle element first (switch or spawner) - ONCE per room, shared by all doors
		var puzzle_element_created = false
		var puzzle_element_data = {}
		
		if puzzle_type == "switch_walk" or puzzle_type == "switch_pillar":
			# Find a valid floor position for switch IN THE PUZZLE ROOM
			var switch_type = "walk" if puzzle_type == "switch_walk" else "pillar"
			var switch_weight = 1.0 if switch_type == "walk" else 5.0
			
			var switch_data = _find_floor_switch_position(room, grid, map_size, rng, -1, -1)
			if switch_data != null and not switch_data.is_empty() and switch_data.has("position"):
				puzzle_element_created = true
				puzzle_element_data = {
					"type": "switch",
					"switch_type": switch_type,
					"switch_weight": switch_weight,
					"switch_data": switch_data,
					"switch_room": room
				}
				LogManager.log("DungeonGenerator: Created switch puzzle for room (" + str(room.x) + ", " + str(room.y) + ") - type: " + str(switch_type), LogManager.CATEGORY_DUNGEON)
			else:
				LogManager.log("DungeonGenerator: WARNING - Could not place floor switch in puzzle room (" + str(room.x) + ", " + str(room.y) + ")! Skipping puzzle.", LogManager.CATEGORY_DUNGEON)
		elif puzzle_type == "enemy":
			# Add enemy spawner IN THE PUZZLE ROOM
			var spawner_positions = []
			for x in range(room.x + 2, room.x + room.w - 2):
				for y in range(room.y + 2, room.y + room.h - 2):
					if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
						if grid[x][y] == 1: # Floor
							var world_x = x * tile_size + tile_size / 2.0
							var world_y = y * tile_size + tile_size / 2.0
							spawner_positions.append({
								"position": Vector2(world_x, world_y),
								"tile_x": x,
								"tile_y": y
							})
			
			if spawner_positions.size() > 0:
				spawner_positions.shuffle()
				var spawner_data = spawner_positions[0]
				puzzle_element_created = true
				puzzle_element_data = {
					"type": "enemy",
					"spawner_data": spawner_data,
					"spawner_room": room
				}
				LogManager.log("DungeonGenerator: Created enemy spawner puzzle for room (" + str(room.x) + ", " + str(room.y) + ") - spawner at " + str(spawner_data.position), LogManager.CATEGORY_DUNGEON)
			else:
				LogManager.log("DungeonGenerator: WARNING - Could not place enemy spawner in puzzle room (" + str(room.x) + ", " + str(room.y) + ")! Skipping puzzle.", LogManager.CATEGORY_DUNGEON)
		
		# CRITICAL SAFETY CHECK: Only create blocking doors if puzzle element was successfully created
		if not puzzle_element_created:
			LogManager.log_error("DungeonGenerator: ERROR - Puzzle element was NOT created for room (" + str(room.x) + ", " + str(room.y) + ") with puzzle_type: " + str(puzzle_type) + "! Skipping ALL doors in this room.", LogManager.CATEGORY_DUNGEON)
			# Remove doors from assigned list since we're not creating the puzzle
			for door in doors_in_room:
				if door in assigned_doors:
					assigned_doors.erase(door)
			continue
		
		# CRITICAL: Verify puzzle_element_data is valid before proceeding
		if puzzle_element_data.is_empty() or not puzzle_element_data.has("type"):
			LogManager.log_error("DungeonGenerator: ERROR - puzzle_element_data is invalid for room (" + str(room.x) + ", " + str(room.y) + ")! puzzle_element_created was true but data is empty!", LogManager.CATEGORY_DUNGEON)
			continue
		
		# Create blocking doors for at least 1 door (minimum), or all doors in the room
		# For now, create blocking doors for ALL doors in the puzzle room
		LogManager.log("DungeonGenerator: Creating blocking doors for room (" + str(room.x) + ", " + str(room.y) + ") with " + str(doors_in_room.size()) + " doors, puzzle type: " + str(puzzle_type) + ", puzzle_element type: " + str(puzzle_element_data.type), LogManager.CATEGORY_DUNGEON)
		for door in doors_in_room:
			# Determine direction based on which WALL of the PUZZLE ROOM the door is on
			var direction = _determine_door_direction_for_puzzle_room(door, room, all_rooms)
			
			# CRITICAL: door.x and door.y are the position in room1, not necessarily in puzzle room
			# Need to calculate the correct position on the puzzle room's wall
			var door_room2 = door.room2 if ("room2" in door and door.room2 and not door.room2.is_empty()) else null
			
			# Determine if puzzle room is room2 (if so, door position needs adjustment)
			var puzzle_is_room2 = false
			if door_room2:
				puzzle_is_room2 = (door_room2.x == room.x and door_room2.y == room.y and \
								  door_room2.w == room.w and door_room2.h == room.h)
			
			# Calculate door position on the puzzle room's wall
			var door_tile_x = door.x
			var door_tile_y = door.y
			var open_tile_x = door_tile_x
			var open_tile_y = door_tile_y
			
			# If puzzle room is room2, the door position needs to be moved to the puzzle room's wall
			if puzzle_is_room2:
				# Door is connecting from room1 to puzzle room (room2)
				# We need to calculate the position on puzzle room's wall based on door direction
				match direction:
					"Up":
						# Door on top wall of puzzle room - door.y should be at puzzle_room.y
						open_tile_x = door_tile_x # Keep same X (horizontal position)
						open_tile_y = room.y # Top wall of puzzle room
					"Down":
						# Door on bottom wall of puzzle room - door.y should be at puzzle_room.y + room.h - 1
						open_tile_x = door_tile_x # Keep same X (horizontal position)
						open_tile_y = room.y + room.h - 1 # Bottom wall of puzzle room
					"Left":
						# Door on left wall of puzzle room - door.x should be at puzzle_room.x
						open_tile_x = room.x # Left wall of puzzle room
						open_tile_y = door_tile_y # Keep same Y (vertical position)
					"Right":
						# Door on right wall of puzzle room - door.x should be at puzzle_room.x + room.w - 1
						open_tile_x = room.x + room.w - 1 # Right wall of puzzle room
						open_tile_y = door_tile_y # Keep same Y (vertical position)
			# else: Puzzle room is room1 - door position is already in puzzle room, use as-is
			
			# Adjust position based on door direction and offset from wall
			# These offsets are relative to the door's position on the wall
			match direction:
				"Up":
					# Door Up (3x2): Open at middle column, top row
					# open_tile_x is already on the wall, adjust to middle column
					open_tile_x = open_tile_x + 1 # Middle column (3 tiles wide, so +1 from left edge)
					open_tile_y = open_tile_y + 0 # Already at top wall (row 0)
				"Right":
					# Door Right (2x3): Open at right column, middle row
					# open_tile_x is already on the wall, adjust to right column
					open_tile_x = open_tile_x + 1 # Right column (already at wall)
					open_tile_y = open_tile_y + 1 # Middle row (3 tiles tall, so +1 from top)
				"Down":
					# Door Down (3x2): Open at middle column, bottom row
					# open_tile_x is already on the wall, adjust to middle column
					open_tile_x = open_tile_x + 1 # Middle column (3 tiles wide, so +1 from left edge)
					open_tile_y = open_tile_y + 1 # Bottom row (2 tiles tall, so +1 from top edge)
				"Left":
					# Door Left (2x3): Open at left column, middle row
					# open_tile_x is already on the wall, adjust to left column
					open_tile_x = open_tile_x + 0 # Left column (already at wall)
					open_tile_y = open_tile_y + 1 # Middle row (3 tiles tall, so +1 from top)
			
			# Calculate world position from open tile (center of tile)
			# This is the OPEN position - door will start here and move to CLOSED position when entering room
			var door_world_x = open_tile_x * tile_size + tile_size / 2.0
			var door_world_y = open_tile_y * tile_size + tile_size / 2.0
			
			# Create door data
			# Position is the OPEN state position (will move to CLOSED when entering room)
			# CRITICAL: Verify room is still a valid puzzle room before creating door
			if not room in room_puzzle_data:
				LogManager.log_error("DungeonGenerator: ERROR - Room (" + str(room.x) + ", " + str(room.y) + ") is no longer in room_puzzle_data! Cannot create door.", LogManager.CATEGORY_DUNGEON)
				continue
			
			# NOTE: door_room1 is already declared at line 1933 and verified to match puzzle room at line 1935-1940
			# No need to re-declare or re-verify here - the door_in_puzzle_room check above already ensures room1 matches
			
			var door_data = {
				"type": door_type,
				"direction": direction,
				"position": Vector2(door_world_x, door_world_y), # OPEN position (tile center)
				"tile_x": open_tile_x,
				"tile_y": open_tile_y,
				"door": door,
				"blocking_room": room, # CRITICAL: This door is IN the puzzle room (the room that has the puzzle)
				"is_closed": false, # Start open, close when entering puzzle room
				"puzzle_type": puzzle_type # "switch_walk", "switch_pillar", or "enemy"
			}
			
			# Store puzzle room as room1 for blocking doors
			door_data.original_room1 = room # Puzzle room is always room1 for blocking doors
			
			LogManager.log("DungeonGenerator: Creating blocking door for puzzle room (" + str(room.x) + ", " + str(room.y) + ") - direction: " + str(direction) + ", open_tile: (" + str(open_tile_x) + "," + str(open_tile_y) + ")", LogManager.CATEGORY_DUNGEON)
			
			# CRITICAL: Add puzzle-specific data from the puzzle_element_data created above (shared across all doors in room)
			# Only add door if puzzle element data is valid
			var door_has_valid_puzzle = false
			if puzzle_element_data.has("type") and puzzle_element_data.type == "switch":
				if puzzle_element_data.has("switch_data") and puzzle_element_data.switch_data.has("position"):
					door_data.floor_switch_position = puzzle_element_data.switch_data.position
					door_data.switch_tile_x = puzzle_element_data.switch_data.tile_x
					door_data.switch_tile_y = puzzle_element_data.switch_data.tile_y
					door_data.switch_room = puzzle_element_data.switch_room
					door_data.requires_switch = true
					door_data.switch_type = puzzle_element_data.switch_type
					door_data.switch_required_weight = puzzle_element_data.switch_weight
					door_has_valid_puzzle = true
					LogManager.log("DungeonGenerator: Added switch data to door - switch at (" + str(door_data.switch_tile_x) + ", " + str(door_data.switch_tile_y) + ")", LogManager.CATEGORY_DUNGEON)
			elif puzzle_element_data.has("type") and puzzle_element_data.type == "enemy":
				if puzzle_element_data.has("spawner_data") and puzzle_element_data.spawner_data.has("position"):
					if not "enemy_spawners" in door_data:
						door_data.enemy_spawners = []
					door_data.enemy_spawners.append({
						"position": puzzle_element_data.spawner_data.position,
						"tile_x": puzzle_element_data.spawner_data.tile_x,
						"tile_y": puzzle_element_data.spawner_data.tile_y,
						"room": puzzle_element_data.spawner_room,
						"spawn_once": true # Only spawn 1 enemy, then destroy spawner
					})
					door_data.requires_enemies = true
					door_has_valid_puzzle = true
					LogManager.log("DungeonGenerator: Added enemy spawner data to door - spawner at (" + str(puzzle_element_data.spawner_data.tile_x) + ", " + str(puzzle_element_data.spawner_data.tile_y) + ")", LogManager.CATEGORY_DUNGEON)
			
			# CRITICAL SAFETY CHECK: Only add door to blocking doors if it has valid puzzle element
			if not door_has_valid_puzzle:
				LogManager.log_error("DungeonGenerator: ERROR - Blocking door for room (" + str(room.x) + ", " + str(room.y) + ") has no valid puzzle element! Skipping door. puzzle_type: " + str(puzzle_type) + ", puzzle_element_data: " + str(puzzle_element_data), LogManager.CATEGORY_DUNGEON)
				continue # Skip this door - don't add it to blocking_doors
			
			# FINAL SAFETY CHECK: Verify door has either requires_switch or requires_enemies set
			if door_data.type == "StoneDoor" or door_data.type == "GateDoor":
				var has_switch = door_data.get("requires_switch", false) == true
				var has_enemies = door_data.get("requires_enemies", false) == true
				if not has_switch and not has_enemies:
					LogManager.log_error("DungeonGenerator: ERROR - Blocking door (StoneDoor/GateDoor) has neither requires_switch nor requires_enemies! Door data: " + str(door_data.keys()) + " - SKIPPING DOOR", LogManager.CATEGORY_DUNGEON)
					continue # Skip this door - it's invalid
			
			# FINAL CRITICAL SAFETY CHECK: Verify door's blocking_room matches the puzzle room exactly
			if door_data.blocking_room.x != room.x or door_data.blocking_room.y != room.y or \
			   door_data.blocking_room.w != room.w or door_data.blocking_room.h != room.h:
				LogManager.log_error("DungeonGenerator: ERROR - Door blocking_room (" + str(door_data.blocking_room.x) + "," + str(door_data.blocking_room.y) + ") doesn't match puzzle room (" + str(room.x) + "," + str(room.y) + ")! This door is for wrong room! SKIPPING DOOR", LogManager.CATEGORY_DUNGEON)
				continue # Skip this door - it's for the wrong room
			
			# Add door to blocking doors list ONLY if it has valid puzzle element
			blocking_doors.append(door_data)
			LogManager.log("DungeonGenerator: Created blocking door for puzzle room (" + str(room.x) + ", " + str(room.y) + ") - direction: " + str(direction) + ", open tile: (" + str(open_tile_x) + ", " + str(open_tile_y) + "), puzzle_type: " + str(puzzle_type) + ", has_switch: " + str(door_data.get("requires_switch", false)) + ", has_enemies: " + str(door_data.get("requires_enemies", false)), LogManager.CATEGORY_DUNGEON)
	
	# STEP 3: Randomly assign some doors as KeyDoors (except start/exit room doors and already assigned doors)
	var key_door_chance = 0.2 # 20% chance per door
	var key_doors_to_create = []
	
	for door in all_doors:
		# Skip if already assigned to a room puzzle
		if door in assigned_doors:
			continue
		
		# Skip doors connected to start or exit rooms
		var door_room1 = door.room1 if "room1" in door else null
		var door_room2 = door.room2 if "room2" in door else null
		
		var is_start_or_exit_door = false
		if door_room1:
			var room1_index = all_rooms.find(door_room1)
			if room1_index == start_room_index or room1_index == exit_room_index:
				is_start_or_exit_door = true
		if door_room2:
			var room2_index = all_rooms.find(door_room2)
			if room2_index == start_room_index or room2_index == exit_room_index:
				is_start_or_exit_door = true
		
		if is_start_or_exit_door:
			continue
		
		if rng.randf() < key_door_chance:
			key_doors_to_create.append(door)
	
	# STEP 4: Create KeyDoors with keys placed BEFORE the keydoor
	# First pass: collect all key doors to determine blocking relationships
	var key_door_data_list = []
	for door in key_doors_to_create:
		# Determine direction
		var direction = ""
		if "dir" in door:
			match door.dir:
				"E": direction = "Right"
				"W": direction = "Left"
				"N": direction = "Up"
				"S": direction = "Down"
				_: direction = _determine_door_direction(door, all_rooms)
		else:
			direction = _determine_door_direction(door, all_rooms)
		
		# Calculate middle tile position
		var door_tile_x = door.x
		var door_tile_y = door.y
		var middle_tile_x = door_tile_x
		var middle_tile_y = door_tile_y
		
		match direction:
			"Down":
				# Door Down (3x2): KeyDoors should be placed on row 0, col 1 (CLOSED state)
				# Row 0 is the upper row (closer to room interior) - this is where KeyDoors start CLOSED
				# When opened with key, they move to row 1 (col 1, row 1) - 16px down
				# BUT: After 180° rotation, we need to adjust Y position UP by 8 pixels (half a tile)
				# to account for sprite alignment - position will be adjusted in door.gd
				middle_tile_x = door_tile_x + 1 # col 1 (middle column)
				middle_tile_y = door_tile_y + 0 # row 0 (upper row - CLOSED state for KeyDoor)
			"Up":
				# Door Up (3x2): door spans 2 tiles tall, wall is at top edge
				# Use the TOP tile (row 0) AT the wall boundary
				middle_tile_x = door_tile_x + 1 # col 1 (middle column)
				middle_tile_y = door_tile_y + 1 # row 0 (TOP tile, AT the wall boundary)
			"Right":
				# Door Right (2x3): door spans 2 tiles, wall is at right edge
				# Use the RIGHT tile (col 1) at the wall boundary, not the left tile
				middle_tile_x = door_tile_x + 0 # col 1 (right column, AT the wall boundary)
				middle_tile_y = door_tile_y + 1 # row 1 (middle row)
			"Left":
				# Door Left (2x3): door spans 2 tiles, wall is at left edge
				# Use the LEFT tile (col 0) at the wall boundary
				middle_tile_x = door_tile_x + 1 # col 0 (left column, AT the wall boundary)
				middle_tile_y = door_tile_y + 1 # row 1 (middle row)
		
		var door_world_x = middle_tile_x * tile_size + tile_size / 2.0
		var door_world_y = middle_tile_y * tile_size + tile_size / 2.0
		
		# Determine which room this door blocks (the room you're entering into)
		var door_room1 = door.room1 if "room1" in door else null
		var door_room2 = door.room2 if "room2" in door else null
		var blocking_room = door_room2 if door_room2 != null else door_room1
		
		# Store door data for second pass
		key_door_data_list.append({
			"door": door,
			"direction": direction,
			"door_world_x": door_world_x,
			"door_world_y": door_world_y,
			"middle_tile_x": middle_tile_x,
			"middle_tile_y": middle_tile_y,
			"blocking_room": blocking_room
		})
	
	# Second pass: find rooms before each door, accounting for other key doors
	for i in range(key_door_data_list.size()):
		var key_door_info = key_door_data_list[i]
		var door = key_door_info.door
		var direction = key_door_info.direction
		var door_world_x = key_door_info.door_world_x
		var door_world_y = key_door_info.door_world_y
		var middle_tile_x = key_door_info.middle_tile_x
		var middle_tile_y = key_door_info.middle_tile_y
		var blocking_room = key_door_info.blocking_room
		
		# Collect other key doors (all key doors except this one)
		var other_key_doors = []
		for j in range(key_door_data_list.size()):
			if i != j:
				other_key_doors.append(key_door_data_list[j].door)
		
		# Find rooms reachable BEFORE this door (for key placement)
		# Pass other key doors so they're also treated as blocking
		var rooms_before_door = _find_rooms_before_door(door, start_room, all_rooms, all_doors, other_key_doors)
		
		# Pick a room for the key (must be reachable before the door)
		var key_room = null
		if rooms_before_door.size() > 0:
			# Exclude start and exit rooms from key placement
			var key_room_candidates = []
			for room in rooms_before_door:
				var room_index = all_rooms.find(room)
				if room_index != start_room_index and room_index != exit_room_index:
					key_room_candidates.append(room)
			
			if key_room_candidates.size() > 0:
				key_room = key_room_candidates[rng.randi() % key_room_candidates.size()]
			else:
				# Fallback: use start room
				key_room = start_room
		else:
			# Fallback: use start room
			key_room = start_room
		
		var door_data = {
			"type": "KeyDoor",
			"direction": direction,
			"position": Vector2(door_world_x, door_world_y),
			"tile_x": middle_tile_x,
			"tile_y": middle_tile_y,
			"door": door,
			"blocking_room": blocking_room,
			"is_closed": true, # KeyDoors always closed
			"key_room": key_room # Room where key is placed (before this door)
		}
		
		blocking_doors.append(door_data)
	
	return {
		"doors": blocking_doors,
		"puzzle_data": room_puzzle_data
	}

func _find_floor_switch_position(room: Dictionary, grid: Array, map_size: Vector2i, rng: RandomNumberGenerator, exclude_door_x: int = -1, exclude_door_y: int = -1) -> Dictionary:
	# Find a valid floor position for a floor switch in the room
	# exclude_door_x, exclude_door_y: Tile coordinates of door to avoid placing switch too close to
	# Returns a dictionary with position (Vector2) and tile_x, tile_y (int) or empty dict if no position found
	var tile_size = 16
	var valid_positions = []
	var min_distance_from_door = 3 # Minimum tiles away from door
	
	# Room interior is from room.x + 2 to room.x + room.w - 2 (excluding 2-tile walls)
	# Also exclude door tiles (grid value 2) to avoid placing switches in doorways
	for x in range(room.x + 2, room.x + room.w - 2):
		for y in range(room.y + 2, room.y + room.h - 2):
			if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
				# Check if it's a floor tile (not a door tile)
				if grid[x][y] == 1: # Floor (not door which is 2)
					# Check if position is too close to door (if door position provided)
					var too_close_to_door = false
					if exclude_door_x >= 0 and exclude_door_y >= 0:
						var distance_x = abs(x - exclude_door_x)
						var distance_y = abs(y - exclude_door_y)
						var distance = max(distance_x, distance_y) # Chebyshev distance (tiles)
						if distance < min_distance_from_door:
							too_close_to_door = true
					
					if not too_close_to_door:
						var world_x = x * tile_size + tile_size / 2.0
						var world_y = y * tile_size + tile_size / 2.0
						valid_positions.append({
							"position": Vector2(world_x, world_y),
							"tile_x": x,
							"tile_y": y
						})
	
	if valid_positions.size() > 0:
		# Pick a random position
		return valid_positions[rng.randi() % valid_positions.size()]
	
	return {}

func _determine_door_direction(door: Dictionary, _all_rooms: Array) -> String:
	# Determine door direction based on door position and connected rooms
	# Door on upper wall = "Up", left wall = "Left", etc.
	# This returns direction relative to room1
	if not "room1" in door or not "room2" in door:
		return "Up" # Default
	
	var room1 = door.room1
	var room2 = door.room2
	
	# Determine which wall the door is on by comparing room positions
	# If room2 is above room1, door is on top wall of room1 (Up)
	# If room2 is below room1, door is on bottom wall of room1 (Down)
	# If room2 is left of room1, door is on left wall of room1 (Left)
	# If room2 is right of room1, door is on right wall of room1 (Right)
	
	var dx = room2.x - room1.x
	var dy = room2.y - room1.y
	
	# Check which direction has the larger difference
	if abs(dy) > abs(dx):
		# Vertical alignment
		if dy < 0:
			return "Up" # room2 is above room1 - door on top wall of room1
		else:
			return "Down" # room2 is below room1 - door on bottom wall of room1
	else:
		# Horizontal alignment
		if dx < 0:
			return "Left" # room2 is left of room1 - door on left wall of room1
		else:
			return "Right" # room2 is right of room1 - door on right wall of room1

func _determine_door_direction_for_puzzle_room(door: Dictionary, puzzle_room: Dictionary, _all_rooms: Array) -> String:
	# Determine which WALL of the PUZZLE ROOM the door is on
	# CRITICAL: door.x and door.y are the position in room1, not necessarily in the puzzle room
	# Need to check which room is the puzzle room and determine the wall based on door direction
	var door_room1 = door.room1 if ("room1" in door and door.room1 and not door.room1.is_empty()) else null
	var door_room2 = door.room2 if ("room2" in door and door.room2 and not door.room2.is_empty()) else null
	
	# Check which room is the puzzle room
	var puzzle_is_room1 = false
	var puzzle_is_room2 = false
	
	if door_room1:
		puzzle_is_room1 = (door_room1.x == puzzle_room.x and door_room1.y == puzzle_room.y and \
						  door_room1.w == puzzle_room.w and door_room1.h == puzzle_room.h)
	
	if door_room2:
		puzzle_is_room2 = (door_room2.x == puzzle_room.x and door_room2.y == puzzle_room.y and \
						  door_room2.w == puzzle_room.w and door_room2.h == puzzle_room.h)
	
	# Get door direction from door.dir
	if "dir" in door:
		var door_dir = door.dir
		
		if puzzle_is_room1:
			# Puzzle room is room1 - door.dir represents the wall of puzzle room directly
			match door_dir:
				"E": return "Right" # Door on right wall of puzzle room
				"W": return "Left" # Door on left wall of puzzle room
				"N": return "Up" # Door on top wall of puzzle room
				"S": return "Down" # Door on bottom wall of puzzle room
		elif puzzle_is_room2:
			# Puzzle room is room2 - door.dir is FROM room1, so flip it
			match door_dir:
				"E": return "Left" # Door on left wall of puzzle room (room2 is to the right of room1)
				"W": return "Right" # Door on right wall of puzzle room (room2 is to the left of room1)
				"N": return "Down" # Door on bottom wall of puzzle room (room2 is below room1)
				"S": return "Up" # Door on top wall of puzzle room (room2 is above room1)
	
	# Fallback: calculate based on door position relative to puzzle room center
	var door_x = door.x
	var door_y = door.y
	var puzzle_center_x = puzzle_room.x + puzzle_room.w / 2.0
	var puzzle_center_y = puzzle_room.y + puzzle_room.h / 2.0
	
	var dx = door_x - puzzle_center_x
	var dy = door_y - puzzle_center_y
	
	# Determine which wall based on which direction from center
	if abs(dy) > abs(dx):
		# Vertical - door is more above/below than left/right
		if dy < 0:
			return "Up" # Door is above puzzle room center - door is on top wall
		else:
			return "Down" # Door is below puzzle room center - door is on bottom wall
	else:
		# Horizontal - door is more left/right than above/below
		if dx < 0:
			return "Left" # Door is left of puzzle room center - door is on left wall
		else:
			return "Right" # Door is right of puzzle room center - door is on right wall

func _place_traps_in_dungeon(all_rooms: Array, start_room_index: int, exit_room_index: int, grid: Array, map_size: Vector2i, rng: RandomNumberGenerator) -> Array:
	# Place 1-2 traps in the dungeon (not in start or exit rooms)
	var traps = []
	var tile_size = 16
	
	# Get eligible rooms (not start, not exit)
	var eligible_rooms = []
	for i in range(all_rooms.size()):
		if i != start_room_index and i != exit_room_index:
			eligible_rooms.append(all_rooms[i])
	
	# If no eligible rooms, return empty array
	if eligible_rooms.size() == 0:
		LogManager.log("DungeonGenerator: No eligible rooms for trap placement", LogManager.CATEGORY_DUNGEON)
		return traps
	
	# Decide number of traps (1 or 2)
	var num_traps = rng.randi_range(1, 2)
	LogManager.log("DungeonGenerator: Placing " + str(num_traps) + " trap(s) in dungeon", LogManager.CATEGORY_DUNGEON)
	
	# Place traps in random eligible rooms
	for trap_idx in range(num_traps):
		if eligible_rooms.size() == 0:
			break
		
		# Pick random room
		var room_index = rng.randi() % eligible_rooms.size()
		var room = eligible_rooms[room_index]
		
		# Remove room from eligible list to avoid placing multiple traps in same room
		eligible_rooms.remove_at(room_index)
		
		# Try to find a valid floor position in the room (not near walls, doors, or blocking cells)
		var attempts = 50
		var trap_placed = false
		
		while attempts > 0 and not trap_placed:
			# Random position in room floor (excluding 2-tile wall border, plus extra safety margin)
			var floor_margin = 3  # Extra margin from walls for safety
			var local_x = rng.randi_range(floor_margin, room.w - floor_margin - 1)
			var local_y = rng.randi_range(floor_margin, room.h - floor_margin - 1)
			var world_x = room.x + local_x
			var world_y = room.y + local_y
			
			# Check if position is valid (floor tile, not blocked)
			if world_x >= 0 and world_x < map_size.x and world_y >= 0 and world_y < map_size.y:
				if grid[world_x][world_y] == 1:  # Floor tile
					# Check if position is not too close to door (avoid blocking doorways)
					var too_close_to_door = false
					# Simplified check - just ensure we're not right at door position
					# (More complex check could look at actual door positions)
					
					if not too_close_to_door:
						# Valid position - place trap
						var trap_world_x = world_x * tile_size + tile_size / 2.0
						var trap_world_y = world_y * tile_size + tile_size / 2.0
						
						traps.append({
							"position": Vector2(trap_world_x, trap_world_y),
							"room": room
						})
						
						trap_placed = true
						LogManager.log("DungeonGenerator: Placed trap at (" + str(trap_world_x) + ", " + str(trap_world_y) + ") in room (" + str(room.x) + ", " + str(room.y) + ")", LogManager.CATEGORY_DUNGEON)
			
			attempts -= 1
		
		if not trap_placed:
			LogManager.log("DungeonGenerator: Failed to place trap in room (" + str(room.x) + ", " + str(room.y) + ") after 50 attempts", LogManager.CATEGORY_DUNGEON)
	
	return traps
