extends SceneTree # Usage: # godot --headless --path demo --script scripts/bake_dungeon.gd -- SEED DEPTH OUT_DIR # # Shells out to bin/genesis --emit=json and bakes the result into two sibling # assets under OUT_DIR: # - mesh_library.tres (tile catalog, same content MLB.build() # produces at runtime — shareable across bakes) # - dungeon_seed_depth.tscn (PackedScene with a GridMap referencing # the MeshLibrary, populated per the # generated dungeon) # # Override the genesis binary path with the GENESIS_BIN env var; default is # "../bin/genesis" relative to the Godot project. const MESH_LIB_NAME := "mesh_library.tres" const MLB = preload("res://scripts/mesh_library_builder.gd") func _init() -> void: var args := OS.get_cmdline_user_args() if args.size() != 3: push_error("usage: -- SEED DEPTH OUT_DIR") quit(1) return var seed := int(args[0]) var depth := int(args[1]) var out_dir := args[2] var bin_path := OS.get_environment("GENESIS_BIN") if bin_path == "": bin_path = ProjectSettings.globalize_path("res://") + "../bin/genesis" bin_path = bin_path.simplify_path() if not DirAccess.dir_exists_absolute(out_dir): var err := DirAccess.make_dir_recursive_absolute(out_dir) if err != OK: push_error("cannot create %s (err %d)" % [out_dir, err]) quit(1) return var stdout: Array = [] var exit_code := OS.execute(bin_path, [ "--seed", str(seed), "--depth", str(depth), "--emit=json", ], stdout, true) if exit_code != 0: push_error("genesis exited %d (bin=%s)" % [exit_code, bin_path]) quit(1) return var grid: Dictionary = JSON.parse_string(stdout[0]) if grid.is_empty(): push_error("genesis produced invalid JSON") quit(1) return # Step 1 — MeshLibrary.tres. var lib_path := "%s/%s" % [out_dir, MESH_LIB_NAME] var save_err := MLB.save_resource(lib_path, true) if save_err != OK: push_error("failed to save %s (err %d)" % [lib_path, save_err]) quit(1) return # Step 2 — PackedScene with populated GridMap. var lib: MeshLibrary = load(lib_path) var root := Node3D.new() root.name = "Dungeon_seed%d_depth%d" % [seed, depth] var gm := GridMap.new() gm.name = "GridMap" gm.mesh_library = lib gm.cell_size = Vector3(1, 1, 1) root.add_child(gm) gm.owner = root var cells_set := _populate(gm, grid) var packed := PackedScene.new() var pack_err := packed.pack(root) if pack_err != OK: push_error("failed to pack scene (err %d)" % pack_err) quit(1) return var scn_path := "%s/dungeon_seed%d_depth%d.tscn" % [out_dir, seed, depth] var scn_err := ResourceSaver.save(packed, scn_path) if scn_err != OK: push_error("failed to save %s (err %d)" % [scn_path, scn_err]) quit(1) return print("wrote %s + %s — %d cells" % [scn_path, lib_path, cells_set]) quit(0) # Decode the base64 layers and stamp tile IDs into the GridMap. Mirrors # arcade_scene.gd _populate_grid_map so the CLI produces the same geometry # as the in-engine path for the same seed/depth. Chasms stay as empty cells. func _populate(gm: GridMap, grid: Dictionary) -> int: var w := int(grid["width"]) var h := int(grid["height"]) var terrain := Marshalls.base64_to_raw(grid["terrain"]) var liquid := Marshalls.base64_to_raw(grid["liquid"]) var count := 0 for y in range(h): for x in range(w): var idx := y * w + x var t := terrain[idx] var liq := liquid[idx] if t == 5 and liq == 3: # T_LIQUID + L_CHASM continue var tile := _tile_for(t, liq) if tile < 0: continue gm.set_cell_item(Vector3i(x, 0, y), tile) count += 1 return count func _tile_for(terrain: int, liquid: int) -> int: match terrain: 1: return MLB.TILE_FLOOR 4: return MLB.TILE_CORRIDOR 3: return MLB.TILE_DOOR 2: return MLB.TILE_WALL 6: return MLB.TILE_BRIDGE 7: return MLB.TILE_STAIRS_UP 8: return MLB.TILE_STAIRS_DOWN 5: match liquid: 1: return MLB.TILE_WATER 2: return MLB.TILE_LAVA 4: return MLB.TILE_BRIMSTONE _: return MLB.TILE_WATER _: return -1