143 lines
4.2 KiB
GDScript
143 lines
4.2 KiB
GDScript
extends Node3D
|
||
|
||
# First-person demo. Generates a grid of chunks (each a 79×29 dungeon) times
|
||
# level_count stacked layers. Default is 1×1 chunks × 3 levels. Bump
|
||
# chunks_x / chunks_y to stress-test the renderer with larger worlds.
|
||
|
||
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_WATER := 1
|
||
const L_LAVA := 2
|
||
const L_CHASM := 3
|
||
const L_BRIMSTONE := 4
|
||
|
||
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
|
||
|
||
const CHUNK_W := 79
|
||
const CHUNK_H := 29
|
||
|
||
@export var base_seed: int = 2028
|
||
@export var depth_start: int = 20
|
||
@export var level_count: int = 3
|
||
@export var level_spacing: float = 6.0
|
||
@export var chunks_x: int = 1
|
||
@export var chunks_y: int = 1
|
||
@export var player_spawn_height: float = 1.1
|
||
|
||
@onready var levels_root: Node3D = $Levels
|
||
@onready var player: CharacterBody3D = $Player
|
||
@onready var fps_overlay: Node = $FPSOverlay if has_node("FPSOverlay") else null
|
||
|
||
var _mesh_library: MeshLibrary
|
||
|
||
func _ready() -> void:
|
||
var t0 := Time.get_ticks_msec()
|
||
_mesh_library = MeshLibraryBuilder.build(true)
|
||
|
||
var total_cells := 0
|
||
var total_chunks := 0
|
||
var spawn_world := Vector3.ZERO
|
||
var spawn_found := false
|
||
|
||
for level_index in range(level_count):
|
||
for cy in range(chunks_y):
|
||
for cx in range(chunks_x):
|
||
var seed := base_seed \
|
||
+ level_index * 1000 \
|
||
+ cy * chunks_x + cx
|
||
var depth := depth_start + level_index
|
||
var gen := BrogueGen.new()
|
||
var grid: Dictionary = gen.generate(seed, depth)
|
||
gen.free()
|
||
|
||
var grid_map := GridMap.new()
|
||
grid_map.name = "L%d_C%d_%d" % [level_index, cx, cy]
|
||
grid_map.mesh_library = _mesh_library
|
||
grid_map.cell_size = Vector3(1, 1, 1)
|
||
grid_map.position = Vector3(
|
||
cx * CHUNK_W,
|
||
-level_index * level_spacing,
|
||
cy * CHUNK_H
|
||
)
|
||
levels_root.add_child(grid_map)
|
||
|
||
var cells := _populate_level(grid_map, grid)
|
||
total_cells += cells
|
||
total_chunks += 1
|
||
|
||
# First up-stair on level 0, chunk (0,0) is the spawn point.
|
||
if not spawn_found and level_index == 0 and cx == 0 and cy == 0:
|
||
var up: Vector2i = grid["stairs_up"] as Vector2i
|
||
if up.x >= 0:
|
||
spawn_world = Vector3(up.x, player_spawn_height, up.y)
|
||
spawn_found = true
|
||
|
||
if not spawn_found:
|
||
spawn_world = Vector3(CHUNK_W * 0.5, player_spawn_height, CHUNK_H * 0.5)
|
||
player.position = spawn_world
|
||
|
||
var build_ms := Time.get_ticks_msec() - t0
|
||
var info := "%d chunks (%dx%d x %d lvls) %d placed cells build=%d ms" % [
|
||
total_chunks, chunks_x, chunks_y, level_count, total_cells, build_ms,
|
||
]
|
||
print(info)
|
||
print("Player spawned at %s" % spawn_world)
|
||
if fps_overlay and fps_overlay.has_method("set_subtitle"):
|
||
fps_overlay.set_subtitle(info)
|
||
|
||
# Place every non-chasm cell into the GridMap. Returns the number of cells
|
||
# actually placed (excludes T_NOTHING and chasms).
|
||
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 placed := 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]
|
||
if t == T_LIQUID and liq == L_CHASM:
|
||
continue
|
||
var tile_id := _tile_for(t, liq)
|
||
if tile_id == -1:
|
||
continue
|
||
grid_map.set_cell_item(Vector3i(x, 0, y), tile_id)
|
||
placed += 1
|
||
return placed
|
||
|
||
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
|
||
_: return TILE_WATER
|
||
_: return -1
|