bgen/demo/scenes/generator.gd
saarsena@gmail.com e45f121fb9 init
2026-04-16 21:04:50 -04:00

136 lines
No EOL
4 KiB
GDScript

@tool
extends Node3D
class_name DungeonGenerator
const T_NOTHING := 0
const T_FLOOR := 1
const T_WALL := 2
const T_DOOR := 3
const T_CORRIDOR := 4
const T_LIQUID := 5
const T_BRIDGE := 6
const T_STAIRS_UP := 7
const T_STAIRS_DOWN := 8
const L_NONE := 0
const L_WATER := 1
const L_LAVA := 2
const L_CHASM := 3
const L_BRIMSTONE := 4
# Match the tile IDs exposed by MeshLibraryBuilder.
const TILE_FLOOR := 0
const TILE_WALL := 1
const TILE_DOOR := 2
const TILE_CORRIDOR := 3
const TILE_WATER := 4
const TILE_LAVA := 5
const TILE_BRIMSTONE := 6
const TILE_BRIDGE := 7
const TILE_STAIRS_UP := 8
const TILE_STAIRS_DOWN := 9
@export var base_seed: int = 2321
@export var depth_start: int = 20
@export var level_count: int = 10
@export var level_spacing: float = 6.0
@export var generate: bool = false:
set(value):
if value and Engine.is_editor_hint():
generate_dungeon()
generate = false
@export var clear_levels: bool = false:
set(value):
if value and Engine.is_editor_hint():
_clear_levels()
clear_levels = false
@onready var levels_root: Node3D = %Levels
var _mesh_library: MeshLibrary
# Populate one level's GridMap from the grid dict. Returns the count of
# chasm cells that were deliberately skipped (for reporting).
func _populate_level(grid_map: GridMap, grid: Dictionary) -> int:
var w: int = grid["width"]
var h: int = grid["height"]
var terrain: PackedByteArray = grid["terrain"]
var liquid: PackedByteArray = grid["liquid"]
var chasm_cells := 0
for y in range(h):
for x in range(w):
var idx := y * w + x
var t: int = terrain[idx]
var liq: int = liquid[idx]
# Chasm liquid renders as an actual see-through pit.
if t == T_LIQUID and liq == L_CHASM:
chasm_cells += 1
continue
var tile_id := _tile_for(t, liq)
if tile_id == -1:
continue # T_NOTHING or other empty — leave unrendered
# GridMap coords: (X, Y, Z). We want dungeon x → X, dungeon y → Z.
grid_map.set_cell_item(Vector3i(x, 0, y), tile_id)
return chasm_cells
func _tile_for(terrain: int, liquid: int) -> int:
match terrain:
T_FLOOR: return TILE_FLOOR
T_CORRIDOR: return TILE_CORRIDOR
T_DOOR: return TILE_DOOR
T_WALL: return TILE_WALL
T_BRIDGE: return TILE_BRIDGE
T_STAIRS_UP: return TILE_STAIRS_UP
T_STAIRS_DOWN: return TILE_STAIRS_DOWN
T_LIQUID:
match liquid:
L_WATER: return TILE_WATER
L_LAVA: return TILE_LAVA
L_BRIMSTONE: return TILE_BRIMSTONE
L_CHASM: return -1 # empty cell — see through to level below
_: return TILE_WATER
_: return -1 # T_NOTHING or unknown
func _clear_levels() -> void:
if levels_root == null:
levels_root = %Levels
for child in levels_root.get_children():
levels_root.remove_child(child)
child.queue_free()
func generate_dungeon():
if levels_root == null:
levels_root = %Levels
if levels_root.get_child_count() > 0:
push_warning("Levels already generated — toggle clear_levels first to regenerate.")
return
_mesh_library = MeshLibraryBuilder.build()
var edited_root: Node = get_tree().edited_scene_root if Engine.is_editor_hint() else null
var total_chasm_cells := 0
for level_index in range(level_count):
var level_seed := base_seed + level_index
var depth := depth_start + level_index
var gen := BrogueGen.new()
var grid: Dictionary = gen.generate(level_seed, depth)
gen.free()
var grid_map := GridMap.new()
grid_map.name = "Level%d" % level_index
grid_map.mesh_library = _mesh_library
grid_map.cell_size = Vector3(1, 1, 1)
grid_map.position.y = -level_index * level_spacing
levels_root.add_child(grid_map)
if edited_root:
grid_map.owner = edited_root
var chasm_cells := _populate_level(grid_map, grid)
total_chasm_cells += chasm_cells
print("Level %d: seed=%d depth=%d rooms=%d machines=%d chasms=%d"
% [level_index, level_seed, depth,
(grid["rooms"] as Array).size(),
(grid["machines"] as Array).size(),
chasm_cells])