delete files in nickes

This commit is contained in:
2026-01-11 03:34:14 +01:00
parent b692b4f39d
commit 9b55af2e67
46 changed files with 2237 additions and 654 deletions

View File

@@ -289,11 +289,14 @@ func generate_dungeon(map_size: Vector2i, seed_value: int = 0, level: int = 1) -
# 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 = _place_blocking_doors(all_rooms, all_doors, grid, map_size, rng, start_room_index, exit_room_index)
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 = []
for door_data in blocking_doors:
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
@@ -333,7 +336,7 @@ func generate_dungeon(map_size: Vector2i, seed_value: int = 0, level: int = 1) -
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)
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)
# NOTE: Stairs placement was moved earlier (step 7.5, before torches) to prevent torch overlap
@@ -550,6 +553,11 @@ func _create_corridor_between_rooms(room1: Dictionary, room2: Dictionary, grid:
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:
@@ -564,6 +572,10 @@ func _create_corridor_between_rooms(room1: Dictionary, room2: Dictionary, grid:
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)
@@ -633,6 +645,10 @@ func _create_corridor_between_rooms(room1: Dictionary, room2: Dictionary, grid:
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:
@@ -647,6 +663,10 @@ func _create_corridor_between_rooms(room1: Dictionary, room2: Dictionary, grid:
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)
@@ -744,35 +764,122 @@ func _place_torches_in_room(room: Dictionary, grid: Array, all_doors: Array, _ma
# 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):
# Check if this is a valid left wall position (use room.x + 1, the right part of the left wall)
if _is_valid_torch_position(room.x + 1, y, grid, all_doors):
# Place at the same distance from floor as top wall torches
# 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)
valid_wall_positions.append({"pos": world_pos, "wall": "left", "rotation": 270})
break # Only add one torch per wall
# 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)
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": "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 (use room.x + room.w - 2, the left part of the right wall)
if _is_valid_torch_position(room.x + room.w - 2, y, grid, all_doors):
# Place at the same distance from floor as top wall torches
# 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)
valid_wall_positions.append({"pos": world_pos, "wall": "right", "rotation": 90})
break # Only add one torch per wall
# 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:
@@ -813,13 +920,25 @@ func _is_valid_torch_position(x: int, y: int, grid: Array, all_doors: Array) ->
for door in all_doors:
var door_x = door.x
var door_y = door.y
var door_w = door.w if "w" in door else 2 # Default door width (2 or 3)
var door_h = door.h if "h" in door else 3 # Default door height (2 or 3)
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
# For horizontal doors: door.w is width (2 or 3), door.h is 1
# For vertical doors: door.w is 1, door.h is height (2 or 3)
if x >= door_x and x < door_x + door_w and y >= door_y and y < door_y + door_h:
if x >= door_x and x < door_x + actual_w and y >= door_y and y < door_y + actual_h:
return false
return true
@@ -1161,14 +1280,34 @@ func _place_enemies_in_room(room: Dictionary, grid: Array, map_size: Vector2i, r
else:
move_speed = rng.randf_range(50.0, 80.0) # Other enemies (humanoids): faster
enemies.append({
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
@@ -1671,22 +1810,39 @@ func _force_place_stairs(exit_room: Dictionary, grid: Array, tile_grid: Array, m
print("DungeonGenerator: Force placed ", stairs_dir, " stairs at tile (", stairs_data.x, ",", stairs_data.y, ") world pos: ", stairs_data.world_pos)
return stairs_data
func _place_interactable_objects_in_room(room: Dictionary, grid: Array, map_size: Vector2i, all_doors: Array, all_enemies: Array, rng: RandomNumberGenerator) -> Array:
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
if room_puzzle_data.size() > 0:
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:
var puzzle_info = room_puzzle_data[puzzle_room]
print("DungeonGenerator: Checking room (", room.x, ",", room.y, ") - puzzle_room (", puzzle_room.x, ",", puzzle_room.y, ") puzzle_type: ", puzzle_info.type)
if puzzle_info.type == "switch_pillar":
has_pillar_switch_puzzle = true
print("DungeonGenerator: Room (", room.x, ",", room.y, ") has pillar switch puzzle - will spawn at least 1 pillar")
break
else:
print("DungeonGenerator: room_puzzle_data is empty for room (", room.x, ",", room.y, ")")
# 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
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)
@@ -1696,6 +1852,14 @@ func _place_interactable_objects_in_room(room: Dictionary, grid: Array, map_size
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"},
@@ -1737,23 +1901,57 @@ func _place_interactable_objects_in_room(room: Dictionary, grid: Array, map_size
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:
push_warning("DungeonGenerator: Room (", room.x, ",", room.y, ") has pillar switch puzzle but NO valid positions! Cannot place pillar.")
return objects
# Shuffle positions to randomize placement
valid_positions.shuffle()
# Place objects
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]
# 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": object_type_data.type,
"setup_function": object_type_data.setup,
"position": position,
"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
@@ -1872,7 +2070,7 @@ func _find_rooms_before_door(door: Dictionary, start_room: Dictionary, _all_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) -> Array:
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 = []
@@ -1892,6 +2090,7 @@ func _place_blocking_doors(all_rooms: Array, all_doors: Array, grid: Array, map_
# 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
print("DungeonGenerator: Assigning puzzles to rooms (", all_rooms.size(), " total rooms, excluding start/exit)")
for i in range(all_rooms.size()):
if i == start_room_index or i == exit_room_index:
continue # Skip start and exit rooms
@@ -1899,47 +2098,40 @@ func _place_blocking_doors(all_rooms: Array, all_doors: Array, grid: Array, map_
var room = all_rooms[i]
if rng.randf() < puzzle_room_chance:
print("DungeonGenerator: Room (", room.x, ", ", room.y, ") selected for puzzle assignment")
# 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 lead OUT OF this room (doors IN this room that exit to other rooms)
# These are doors where room1 == this room (doors that start FROM this puzzle room)
var doors_out_of_room = []
# Find all doors that are connected to this puzzle room
var doors_in_room = []
for door in all_doors:
# CRITICAL: Find doors where room1 == this room (doors that lead OUT OF this room)
if not "room1" in door or not door.room1 or door.room1.is_empty():
continue
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 door_room1 = door.room1
# Compare rooms by position and size (value comparison, not reference)
var door_leads_out_of_this_room = (door_room1.x == room.x and door_room1.y == room.y and \
door_room1.w == room.w and door_room1.h == room.h)
var room_matches = false
if door_leads_out_of_this_room:
# CRITICAL: Also check that this door doesn't lead into start or exit room
if not "room2" in door or not door.room2 or door.room2.is_empty():
continue
var door_room2 = door.room2
var door_room2_index = -1
for j in range(all_rooms.size()):
var check_room = all_rooms[j]
if check_room.x == door_room2.x and check_room.y == door_room2.y and \
check_room.w == door_room2.w and check_room.h == door_room2.h:
door_room2_index = j
break
# Skip if door leads into start or exit room
if door_room2_index == start_room_index or door_room2_index == exit_room_index:
continue
doors_out_of_room.append(door)
# 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_out_of_room.size() == 0:
continue # No doors leading out of this room, skip
if doors_in_room.size() == 0:
print("DungeonGenerator: Room (", room.x, ", ", room.y, ") has no doors connected - skipping puzzle assignment")
continue # No doors connected to this room, skip
print("DungeonGenerator: Room (", room.x, ", ", room.y, ") has ", doors_in_room.size(), " doors - assigning puzzle")
# Decide puzzle type: 33% walk switch, 33% pillar switch, 33% enemy spawner (if room is large enough)
var can_have_enemies = false
@@ -1959,13 +2151,16 @@ func _place_blocking_doors(all_rooms: Array, all_doors: Array, grid: Array, map_
# Store puzzle data for this room
room_puzzle_data[room] = {
"type": puzzle_type,
"doors": doors_out_of_room
"doors": doors_in_room
}
print("DungeonGenerator: Stored puzzle data for room (", room.x, ", ", room.y, ") - type: ", puzzle_type, ", doors: ", doors_in_room.size())
# Mark these doors as assigned
for door in doors_out_of_room:
for door in doors_in_room:
assigned_doors.append(door)
print("DungeonGenerator: Assigned puzzles to ", room_puzzle_data.size(), " rooms")
# 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!
@@ -2073,95 +2268,71 @@ func _place_blocking_doors(all_rooms: Array, all_doors: Array, grid: Array, map_
# For now, create blocking doors for ALL doors in the puzzle room
print("DungeonGenerator: Creating blocking doors for room (", room.x, ", ", room.y, ") with ", doors_in_room.size(), " doors, puzzle type: ", puzzle_type, ", puzzle_element type: ", puzzle_element_data.type)
for door in doors_in_room:
# CRITICAL: Verify this door is in the puzzle room (already checked above, but double-check)
if not "room1" in door or not door.room1 or door.room1.is_empty():
push_error("DungeonGenerator: ERROR - Door in puzzle room (", room.x, ", ", room.y, ") has no room1! Skipping door.")
continue
# 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)
var door_room1 = door.room1
# CRITICAL: Verify door.room1 matches the puzzle room EXACTLY (value comparison, not reference)
var door_in_puzzle_room = (door_room1.x == room.x and door_room1.y == room.y and \
door_room1.w == room.w and door_room1.h == room.h)
# 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
if not door_in_puzzle_room:
push_error("DungeonGenerator: ERROR - Door room1 (", door_room1.x, ", ", door_room1.y, ") does NOT match puzzle room (", room.x, ", ", room.y, ")! Skipping door.")
continue # This door is not in the puzzle room, skip - DO NOT CREATE DOOR
# 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)
# CRITICAL: Verify this door is not already assigned to another puzzle room
# (This should never happen, but safety check)
if door in assigned_doors:
# Check if this door was assigned to a different room
var already_in_different_room = false
for other_room in room_puzzle_data.keys():
if other_room.x != room.x or other_room.y != room.y:
# This is a different puzzle room - check if door belongs to it
var other_puzzle_info = room_puzzle_data[other_room]
if door in other_puzzle_info.doors:
already_in_different_room = true
break
if already_in_different_room:
push_error("DungeonGenerator: ERROR - Door already assigned to a different puzzle room! Skipping door.")
continue # Door is already in another puzzle room - DO NOT CREATE DOOR HERE
# CRITICAL: Check that this door doesn't lead into start/exit room
if "room2" in door and door.room2 and not door.room2.is_empty():
var door_room2 = door.room2
var door_room2_index = -1
for j in range(all_rooms.size()):
var check_room = all_rooms[j]
if check_room.x == door_room2.x and check_room.y == door_room2.y and \
check_room.w == door_room2.w and check_room.h == door_room2.h:
door_room2_index = j
break
if door_room2_index == start_room_index or door_room2_index == exit_room_index:
print("DungeonGenerator: ERROR - Door leads into start/exit room! Skipping blocking door creation.")
continue
# Determine direction based on door's dir field (E/W/N/S) or calculate from room positions
var direction = ""
if "dir" in door:
# Map door direction to our direction enum
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 door position based on new rules:
# Open state positions:
# - UP: tile 2 (row 0, col 2) = door_x+2, door_y+0
# - RIGHT: tile 4 (col 1, row 1) = door_x+1, door_y+1
# - DOWN: tile 5 (row 1, col 1) = door_x+1, door_y+1 (middle column, not rightmost)
# - LEFT: tile 3 (col 1, row 0) = door_x+1, door_y+0
# 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 tile 2 (row 0, col 2) = door_x+2, door_y+0
open_tile_x = door_tile_x + 1 # col 2 (middle column, not rightmost)
open_tile_y = door_tile_y + 0 # row 0 (top row)
# 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 tile 4 (col 1, row 1) = door_x+1, door_y+1
open_tile_x = door_tile_x + 1 # col 1 (right column)
open_tile_y = door_tile_y + 1 # row 1 (middle row)
# 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): StoneDoor/GateDoor start OPEN at (col 1, row 1) = door_x+1, door_y+1
# When entering room, they CLOSE to (col 1, row 0) = door_x+1, door_y+0 (16px up from open)
# When solving puzzle, they OPEN back to (col 1, row 1) = door_x+1, door_y+1
open_tile_x = door_tile_x + 1 # col 1 (middle column, not rightmost)
open_tile_y = door_tile_y + 1 # row 1 (bottom row - OPEN state, closer to wall)
# 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 tile 3 (col 1, row 0) = door_x+1, door_y+0
open_tile_x = door_tile_x + 0 # col 0 (left column)
open_tile_y = door_tile_y + 1 # row 1 (middle row)
# 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
@@ -2190,18 +2361,10 @@ func _place_blocking_doors(all_rooms: Array, all_doors: Array, grid: Array, map_
"puzzle_type": puzzle_type # "switch_walk", "switch_pillar", or "enemy"
}
# CRITICAL: Store room1 and room2 from original door for verification
# Ensure room1 matches blocking_room (puzzle room)
if "room1" in door and door.room1:
door_data.original_room1 = door.room1
# CRITICAL: Verify room1 matches puzzle room
if not (door.room1.x == room.x and door.room1.y == room.y and door.room1.w == room.w and door.room1.h == room.h):
push_error("DungeonGenerator: ERROR - door.room1 doesn't match puzzle room! room1: (", door.room1.x, ",", door.room1.y, "), puzzle: (", room.x, ",", room.y, ")")
if "room2" in door and door.room2:
door_data.original_room2 = door.room2
# Store puzzle room as room1 for blocking doors
door_data.original_room1 = room # Puzzle room is always room1 for blocking doors
var door_room2_str = "(" + str(door.room2.x) + "," + str(door.room2.y) + ")" if "room2" in door and door.room2 else "(?,?)"
print("DungeonGenerator: Creating blocking door for puzzle room (", room.x, ", ", room.y, ") - door.room1: (", door_room1.x, ",", door_room1.y, "), door.room2: ", door_room2_str, ", direction: ", direction, ", open_tile: (", open_tile_x, ",", open_tile_y, ")")
print("DungeonGenerator: Creating blocking door for puzzle room (", room.x, ", ", room.y, ") - direction: ", direction, ", open_tile: (", open_tile_x, ",", open_tile_y, ")")
# 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
@@ -2251,21 +2414,9 @@ func _place_blocking_doors(all_rooms: Array, all_doors: Array, grid: Array, map_
push_error("DungeonGenerator: ERROR - Door blocking_room (", door_data.blocking_room.x, ",", door_data.blocking_room.y, ") doesn't match puzzle room (", room.x, ",", room.y, ")! This door is for wrong room! SKIPPING DOOR")
continue # Skip this door - it's for the wrong room
# FINAL CRITICAL SAFETY CHECK: Verify door.room1 matches puzzle room (door should be IN puzzle room)
if not "room1" in door or not door.room1 or door.room1.is_empty():
push_error("DungeonGenerator: ERROR - Door has no room1! Cannot verify it's in puzzle room! SKIPPING DOOR")
continue
var final_room1_check = (door.room1.x == room.x and door.room1.y == room.y and \
door.room1.w == room.w and door.room1.h == room.h)
if not final_room1_check:
push_error("DungeonGenerator: ERROR - Door room1 (", door.room1.x, ",", door.room1.y, ") doesn't match puzzle room (", room.x, ",", room.y, ")! This door is NOT in the puzzle room! SKIPPING DOOR")
continue # Skip this door - it's not in the puzzle room
# Add door to blocking doors list ONLY if it has valid puzzle element AND is in correct room
# Add door to blocking doors list ONLY if it has valid puzzle element
blocking_doors.append(door_data)
print("DungeonGenerator: Created blocking door for puzzle room (", room.x, ", ", room.y, ") - direction: ", direction, ", open tile: (", open_tile_x, ", ", open_tile_y, "), puzzle_type: ", puzzle_type, ", has_switch: ", door_data.get("requires_switch", false), ", has_enemies: ", door_data.get("requires_enemies", false), ", door.room1: (", door.room1.x, ",", door.room1.y, "), door.room2: (", door.room2.x if "room2" in door and door.room2 else 0, ",", door.room2.y if "room2" in door and door.room2 else 0, ")")
print("DungeonGenerator: Created blocking door for puzzle room (", room.x, ", ", room.y, ") - direction: ", direction, ", open tile: (", open_tile_x, ", ", open_tile_y, "), puzzle_type: ", puzzle_type, ", has_switch: ", door_data.get("requires_switch", false), ", has_enemies: ", door_data.get("requires_enemies", false))
# 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
@@ -2385,7 +2536,10 @@ func _place_blocking_doors(all_rooms: Array, all_doors: Array, grid: Array, map_
blocking_doors.append(door_data)
return blocking_doors
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
@@ -2429,6 +2583,7 @@ func _find_floor_switch_position(room: Dictionary, grid: Array, map_size: Vector
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
@@ -2436,10 +2591,10 @@ func _determine_door_direction(door: Dictionary, _all_rooms: Array) -> String:
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 (Up)
# If room2 is below room1, door is on bottom wall (Down)
# If room2 is left of room1, door is on left wall (Left)
# If room2 is right of room1, door is on right wall (Right)
# 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
@@ -2448,12 +2603,73 @@ func _determine_door_direction(door: Dictionary, _all_rooms: Array) -> String:
if abs(dy) > abs(dx):
# Vertical alignment
if dy < 0:
return "Up" # room2 is above room1
return "Up" # room2 is above room1 - door on top wall of room1
else:
return "Down" # room2 is below room1
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
return "Left" # room2 is left of room1 - door on left wall of room1
else:
return "Right" # room2 is right of room1
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