# Introduction and Tech Stack

Underroot is a 2D idle/incremental survival-crafting game built in Godot 4. Below: what it is, why Godot and GDScript, and a map of the systems the rest of these docs cover.

## What Underroot is

You play a lone digger. Tunnel down for materials, grow a village on the surface, hold back the Maw — a creature that advances from the right and eats the world one wall at a time. Progress is depth reached, days survived, and village population. It persists across sessions: resources keep draining and threats keep advancing while you're away.

The project name and tagline are set in `project.godot`:

```text
config/name="Underroot"
config/description="Every layer buys time."
config/version="1.0"
```

The publisher/company is Swavvy AB (`export_presets.cfg`, `application/company_name`).

## Engine and language

- **Engine:** Godot 4, targeting engine version **4.6** — `project.godot` declares `config/features=PackedStringArray("4.6", "Forward Plus")`. CI pins the exact toolchain to **4.6.2** (`.github/workflows/ci.yml`, `GODOT_VERSION: 4.6.2`).
- **Renderer:** Forward+ (`"Forward Plus"` in `config/features`).
- **Language:** GDScript only — there is no .NET/C# in the project.
- **Typing:** Strict static typing throughout. Scripts use typed variables, typed parameters, and explicit return types; the parse-check gate (`tools/parse_check.ps1`) fails the build on any type or identifier error.

## Rendering approach

Underroot draws almost all of its world and UI procedurally rather than with sprites. World nodes and UI panels implement `_draw()` and paint shapes directly (see `scenes/world/World.gd`, whose `_draw()` renders the underground layer bands, depth fog, surface strip, ambient day/night tint, and the pond). Static image textures are reserved for story art and cutscenes — for example the intro slideshow images loaded in `scenes/ui/IntroStory.gd`.

Because the UI is procedural, nearly every control is built in code inside `_ready()` via `add_child()`; there are no `.tscn` files for UI nodes apart from the entry scenes (`IntroStory.tscn`, `TitleScreen.tscn`).

## Display and viewport

The default viewport is **1280×720** (`project.godot`):

```text
window/size/viewport_width=1280
window/size/viewport_height=720
window/stretch/mode="canvas_items"
window/stretch/aspect="expand"
```

The stretch aspect is `expand`, so overlays that must fill the screen should read the live viewport size (`get_viewport_rect().size`) rather than assume a fixed 1280×720. The default clear color is a dark violet, `Color(0.07, 0.05, 0.15, 1)`.

## World coordinate convention

Underroot's world is Y-down: the ground surface is world Y **0**, and underground is **positive Y** (tiles descend as Y increases). The tile size is `TILE_SIZE = 32` pixels, defined once as `GameConstants.TILE_SIZE` in `scripts/core/GameConstants.gd`; controller and display scripts source it from there. World bounds and zone landmarks live in `scenes/world/World.gd` (`WORLD_LEFT := -3200.0`, `WORLD_RIGHT := 1000.0`, the forest to the left of 0, defenses near `DEFENSE_LEFT := 250.0`, and the Maw front at `MAW_FRONT_X := 700.0`).

## High-level system map

Underroot is organized around a large set of autoload singletons (33 of them, listed in `project.godot`). They divide roughly into these areas:

| Area | Representative autoloads | Role |
|---|---|---|
| Core runtime | `EventBus`, `GameManager`, `SaveManager`, `TimeManager` | Signals, run state, persistence, the day clock |
| World and digging | `WorldManager`, `RockManager`, `DiscoveryManager`, `BuildManager` | Tile grid, walls, surface rock, discovery pockets, wall stacking |
| The threat | `MawController`, `MawAdaptation`, `WeatherManager`, `AstrolabeManager` | Multi-front Maw, per-material familiarity, storms, ritual escalation |
| Village and survival | `SurvivalManager`, `VillageManager`, `ProjectManager`, `SpecialistManager` | Food/water/fuel drain, population, villager tasks, specialists |
| Economy and content | `DataRegistry`, `Inventory`, `ToolState`, `MachineManager`, `CraftingUnlockManager` | JSON data, resources, tools, machines, recipe unlocks |
| Idle/offline | `TaskQueue`, `OfflineSimulator` | Queued tasks and catch-up simulation on resume |
| Meta and cosmetics | `CosmeticManager`, `CodeManager`, `ChallengeManager`, `HarrowManager`, `SessionTelemetry` | Account-wide unlocks, redemption codes, challenge modifiers, local telemetry |

All cross-system communication flows through the `EventBus` autoload (`scripts/core/EventBus.gd`); scene nodes do not connect signals directly to one another.

Two scripts run before anything else in the autoload order and exist purely to prepare the environment: `FontSetup` (attaches embedded fallback fonts, since Web builds have no OS fonts) and `PerfSetup` (applies Low Performance Mode at startup before any renderer reads `PerformanceMode.enabled`).

## Persistence at a glance

Saves are plain JSON flat files. There are 12 save slots (`user://saves/slot_N.json`; `SaveManager.MAX_SLOTS = 12`), plus account-level `config.json` and `settings.json`. The save format is versioned — `SaveManager.SAVE_VERSION = 3` — with in-place `_migrate()` upgrades and corrupt-slot quarantine so a bad file never blocks boot. On Windows the user data directory is `%APPDATA%\Godot\app_userdata\Underroot\`.

## Key files

- `project.godot` — engine config: name, version, features (4.6 / Forward+), viewport, autoload list.
- `export_presets.cfg` — Web, Windows Desktop, and Linux export targets.
- `scripts/core/GameConstants.gd` — canonical constants, including `TILE_SIZE`.
- `scenes/world/World.gd` — the world root; procedural `_draw()` and the per-frame heartbeat.
- `scripts/core/EventBus.gd` — the single hub for cross-system signals.

## Related

- [Repository Layout and Boot Flow](/docs/underroot/repository-layout-and-boot-flow)
- [Local Setup and Running](/docs/underroot/local-setup-and-running)
- [The Scene Tree](/docs/underroot/the-scene-tree)
- [The Autoload Model](/docs/underroot/the-autoload-model)
- [World Coordinate System and Camera](/docs/underroot/world-coordinate-system-and-camera)