Repository Layout and Boot Flow

The top-level directory map, and how the game boots: from the main scene in project.godot, through the intro and title screens, into gameplay. Autosave too.

Top-level directories

Directory Contents
scripts/ All GDScript logic that is not attached to a scene: autoload singletons, resources, and per-system managers.
scenes/ Scene scripts and the few .tscn scene files. Subfolders: ui/, world/, village/, forest/, maw/, main/.
data/ JSON data files loaded at startup by DataRegistry — materials, tools, recipes, machines, balance, and more.
tools/ Headless verification scripts: parse_check.ps1, validate_data.gd, smoke_test.gd.
assets/ Art, fonts, music, and the UI theme (assets/ui/underroot.tres).
addons/ The one bundled editor addon, copy_all_errors (enabled in project.godot).
.github/workflows/ ci.yml — the three headless CI gates.

The scripts/ layout

scripts/
  core/       EventBus, GameManager, SaveManager, ToolState, TimeManager, AudioManager,
              CraftingUnlockManager, GameConstants, AstrolabeManager, WeatherManager,
              PerfMonitor, PerformanceMode, PerfSetup, FontSetup, CosmeticManager,
              CodeManager, ChallengeManager, HarrowManager, SessionTelemetry, TutorialNudges
  resources/  DataRegistry, Inventory, MaterialDef, ToolDef, RecipeDef
  world/      WorldManager, RockManager, TerrainDigging, DiscoveryManager, LayerGenerator,
              TileData, SurfaceRock
  building/   BuildManager, WallData
  maw/        MawController, MawAdaptation
  forest/     ForestManager, ForestNode, BerryBush
  village/    SurvivalManager, VillageManager, ProjectManager, SpecialistManager
  machines/   MachineManager, MachineData
  idle/       TaskQueue, OfflineSimulator
  ui/         GameOverScreen, OfflineSummaryController, UITheme, HudShell, LayerNav

UITheme (tokens + tooltips), HudShell (HUD-skin state), and LayerNav (shared click-to-travel) are static class_name helpers, not autoloads — see The Talismans HUD.

The scenes/ layout

scenes/
  ui/          HUD, menus, overlays, popups, plus IntroStory.tscn and TitleScreen.tscn
  village/     VillageBackdrop (procedural surface scenery)
  world/       World.tscn + dig/gather/build/particle controllers and terrain display
  forest/      ForestDisplay
  maw/         MawDisplay
  main/        Main.tscn / Main.gd — the runtime root

Most UI is built procedurally in code, so scenes/ui/ holds many .gd scripts that are attached to plain CanvasLayer/Control/Node2D instances at runtime rather than authored .tscn files. The two authored entry scenes are IntroStory.tscn and TitleScreen.tscn. Both HUD skins live here side by side — the classic surfaces (MawBar, VillageBar, BottomBar) and the talisman instruments (MawMedallion, HearthCrest, DiggerCharm, MaterialRail, LedgerOverlay, SpeechBubbleLayer).

Boot flow

The engine starts at the scene named in project.godot:

run/main_scene="res://scenes/ui/IntroStory.tscn"

The chain is: IntroStory → TitleScreen → Main.

1. Autoloads initialize

Before any scene runs, Godot instantiates the 33 autoloads in the order listed in project.godot. Order matters here: FontSetup and PerfSetup come first so fonts and Low Performance Mode are ready before any renderer draws, and data/state singletons (DataRegistry, SaveManager, Inventory, and the world/village managers) load ahead of the scenes that read them.

2. IntroStory — the studio card and story slideshow

scenes/ui/IntroStory.gd (a Node2D) plays a procedural "SWAVVY presents" pixel-tile title card, then fades through five story images (underroot_story_1.pngunderroot_story_5.png) with per-image display times. It runs a small _state machine (card_show → card_fade → fade_in → hold → fade_out) in _process() and draws each frame in _draw(). A "SKIP ›" button and the Space/Enter/Esc keys call _finish(), which advances the scene:

func _finish() -> void:
	if _state == "done":
		return
	_state = "done"
	get_tree().change_scene_to_file("res://scenes/ui/TitleScreen.tscn")

3. TitleScreen — save slots and run entry

scenes/ui/TitleScreen.gd reads the save slots via SaveManager.list_slots() and decides what to show:

Every path into gameplay — new game, continue, send a relative, start fresh — ends in the same transition:

get_tree().change_scene_to_file("res://scenes/main/Main.tscn")

The title screen also hosts the TOP RUNS ledger, Import Save, and (once unlocked) the Challenges selection overlay.

4. Main — the runtime root

scenes/main/Main.tscn is a single Node running scenes/main/Main.gd. Its _ready() instantiates the World.tscn scene and then adds every runtime overlay CanvasLayer (offline summary, game-over screen, cutscenes, weather, popups). Main also decides whether to run offline catch-up (_check_offline_return()), shows breach toasts, and either starts normal music or defers to Wren's tutorial intro on a fresh run.

The full node tree that World.tscn and Main.gd build is documented in The Scene Tree.

Autosave

Autosave is owned by Main.gd, not the world. It ticks a timer in _process() on a fixed interval:

const AUTO_SAVE_INTERVAL := 20.0

func _process(delta: float) -> void:
	_save_timer -= delta
	if _save_timer <= 0.0:
		_save_timer = AUTO_SAVE_INTERVAL
		SaveManager.save_game()

So the game persists to the active slot every 20 seconds. Main.gd also saves on a clean window close (_notification(NOTIFICATION_WM_CLOSE_REQUEST)), warning first via a ConfirmModal if crafts are still underway (GameManager.crafts_underway > 0).

The window is set with get_tree().set_auto_accept_quit(false) in Main._ready(), so the close request is Underroot's to handle — quitting always routes through SaveManager.save_game() first.

Key files