# Dst Worldgen

> Configure Don't Starve Together worldgen from a mod — set world options (hounds="never", antliontribute="never", world_size, task_set, ...) with their exact legal values, and manipulate levels, task sets, tasks and rooms. Use when writing modworldgenmain.lua, AddLevel/AddTaskSet/AddTask/AddRoom, any *PreInit worldgen hook, a level `overrides` table, or when a world generates with the wrong shape/content. Also for checking whether a trimmed world still supports boss chains (Celestial Champion, Ancient Fuelweaver) and for inspecting/debugging world session save files.

- Skill: `rafaelpadovezi/dst-worldgen` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add rafaelpadovezi/dst-worldgen`
- Raw SKILL.md: https://api.skillmd.com/api/skills/rafaelpadovezi/dst-worldgen/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: rafaelpadovezi (https://skillmd.com/u/rafaelpadovezi)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/rafaelpadovezi/dst-worldgen

---


# DST worldgen

Two separate jobs, often confused:

1. **Options** — the `overrides` table on a level (`hounds = "never"`). Each key
   has a *fixed vocabulary*; a wrong value fails silently or crashes worldgen.
   → `references/worldgen-overrides.md`, `references/worldsettings-overrides.md`
2. **Structure** — levels, task sets, tasks, rooms; what actually shapes the map.
   → `references/levels-tasksets-tasks.md`
3. **Verification** — whether the generated world still contains every link a
   boss or system needs, checked against a real save.
   → `references/boss-prerequisites.md`, `references/save-files.md`,
   `scripts/save_inspect.lua`

## Find the game's scripts first

Everything here is verified against DST's shipped, unobfuscated
`data/scripts/`. Read the vanilla definition before overriding it — a `PreInit`
assignment **replaces** a field wholesale rather than merging, so the original
file is the only record of what you are discarding.

Typical locations:

```
~/.steam/steam/steamapps/common/Don't Starve Together/data/scripts   # Linux
~/Library/Application Support/Steam/steamapps/common/Don't Starve Together/data/scripts   # macOS
C:\Program Files (x86)\Steam\steamapps\common\Don't Starve Together\data\scripts          # Windows
```

If you cannot find them, ask the user for the path rather than guessing at
option names or values.

## Part 1 — options

Two categories, and the distinction matters:

| | worldgen | worldsettings |
|---|---|---|
| category | `LEVELCATEGORY.WORLDGEN` | `LEVELCATEGORY.SETTINGS` |
| consumed by | `map/forest_map.lua` during generation | `worldsettings_overrides.lua` → `TUNING` |
| effect | terrain, which prefabs get placed, map shape | spawn rates, timers, damage, boss behaviour |
| changeable later | no — requires a new world | yes |
| example | `spiders`, `world_size`, `task_set` | `hounds`, `antliontribute` |

Both go in the same `overrides` table on a level; `AddModLevel` splits them for
you (`map/levels.lua:720-744`).

```lua
-- modworldgenmain.lua
AddLevel(LEVELTYPE.SURVIVAL, {
    id = "MYMOD_FOREST",
    name = "My World",
    location = "forest",          -- required, no default
    version = 2,
    overrides = {
        task_set    = "MYMOD_TASKSET",  -- required on every level
        world_size  = "small",          -- worldgen
        spiders     = "never",          -- worldgen
        hounds      = "never",          -- worldsettings
        antliontribute = "never",       -- worldsettings
    },
})
```

To change an option on a **vanilla** level instead of shipping your own:

```lua
AddLevelPreInit("SURVIVAL_TOGETHER", function(level)
    level.overrides.hounds = "never"
    level.overrides.antliontribute = "never"
end)
```

### The vocabularies

Look the key up in the reference tables — never assume. The three common lists:

| vocabulary | values | used by |
|---|---|---|
| `worldgen_frequency_descriptions` | `never` `rare` `uncommon` `default` `often` `mostly` `always` `insane` | most worldgen keys |
| `frequency_descriptions` | `never` `rare` `default` `often` `always` | most worldsettings keys |
| `yesno_descriptions` | `never` `default` | `roads`, `terrariumchest`, `mutated_hounds`, … |

The worldgen list is 8 values, the worldsettings list is **5** — `uncommon`,
`mostly` and `insane` are *not* valid for `hounds`, `antliontribute` or any
other worldsettings key. The tables in `references/` give the exact list per
option, per world.

**`"none"` is almost never what you want.** It is not in any of the three lists
above; only a handful of keys with bespoke vocabularies accept it —
`petrification` (`none/few/default/many/max`), `extrastartingitems`, and the
options using the `enableddisabled` / `ghostenabled` / `resetime` lists
(`portalresurection`, `basicresource_regrowth`, `ghostsanitydrain`, …), several
of which *default* to `"none"`. To switch something off, the value is
`"never"`.

`ocean_waterplant` and `ocean_seastack` take the `ocean_`-prefixed vocabulary
(`ocean_never` … `ocean_insane`). `ocean_otterdens`, `ocean_wobsterden`,
`ocean_shoal` and `ocean_bullkelp` take the plain one — they live in the
worldgen *animals*/*resources* groups (`map/customize.lua:303,372`).

### Invalid values fail three different ways — only one is loud

| the key | what happens |
|---|---|
| a worldgen key whose prefabs are in `MULTIPLY_PREFABS` (`forest_map.lua:150-166`) | **hard Lua crash**: `MULTIPLY[v]` is nil, then `MULTIPLY_PREFABS[prefab](v, nil)` does `if mult > 1` → *attempt to compare nil with number* at `forest_map.lua:133` |
| any other worldgen key in `TRANSLATE_TO_PREFABS` | `MULTIPLY[v]` nil → `translated_prefabs[prefab] = nil` → **silent no-op** (`forest_map.lua:277-291`) |
| a worldsettings key | `OverrideTuningVariables(nil)` returns immediately → **silent no-op** (`worldsettings_overrides.lua:2-12`) |

The crash case is unrecoverable: it errors out of `worldgen_main.lua` before the
5-attempt retry loop (`worldgen_main.lua:418-439`) can run. On the caves shard
that means worldgen never finishes, the shard never connects, and the client
hangs at init. Prefabs that trigger it: `slurper_spawner`, `worm_spawner`,
`monkeybarrel_spawner`, `tumbleweedspawner`, `buzzardspawner`,
`wobster_den_spawner_shore`, `oceanfish_shoalspawner`, `seastack_spawner_*`,
`waterplant_spawner_rough`, `boat_otterden`.

`touchstone` and `boons` index `MULTIPLY` directly too
(`worldgen_main.lua:358-372`) and crash the same way.

### Regenerating the tables

`references/*-overrides.md` are generated, not hand-written. After a game patch:

```bash
cd "<path to data/scripts>"
luajit /path/to/scripts/dump_options.lua --format=md    # or --format=tsv
```

It loads the game's real `map/customize.lua` in a stubbed environment, so the
output is whatever the current build actually accepts.

## Part 2 — structure

Read `references/levels-tasksets-tasks.md` for the field-by-field detail. The
essentials:

**Never redefine, always PreInit.** `AddTask` and `AddRoom` *assert* on a
duplicate name (`map/tasks.lua:58`, `map/rooms.lua:20`); the mod versions
`AddModTask`/`AddModRoom` only `moderror` and **silently drop your definition**.
So to change vanilla content use `AddTaskPreInit` / `AddRoomPreInit` /
`AddLevelPreInit` / `AddTaskSetPreInit`, and to add new content use a fresh,
mod-namespaced name.

**Worldgen hooks belong in `modworldgenmain.lua`,** not `modmain.lua` —
worldgen runs in its own Lua state. Runtime-only additions (components,
spawners) go in `modmain.lua`.

**A broken lock/key chain does not crash.** `Story:LinkNodesByKeys` attaches a
random task when nothing is unlockable (`storygen.lua:596-598`), so the map
silently loses its intended shape. Pick locks that some included task's
`keys_given` actually provides — `LOCKS_KEYS` in `map/lockandkey.lua` says what
opens what.

**`valid_start_tasks` does not pick the topology root.** It only places the
player portal (`storygen.lua:916-940`). The root is a *random* task with empty
or `LOCKS.NONE` locks (`Story:_FindStartingTask`, `storygen.lua:854-862`).

**Task-set bookkeeping must stay consistent.** `set_pieces[*].tasks` must name
tasks actually in the set, and task-set `required_prefabs` must be deliverable
by some room or setpiece in it — otherwise worldgen PANICs and rerolls, or
worse, ships a world missing content. Klei's own warning sits at
`map/tasksets/caves.lua:86`.

### Shrinking a world

In order of effect: fewer tasks in the task set (each task ≈ one biome blob) →
fewer rooms per task (fixed `room_choices` counts, not
`math.random(SIZE_VARIATION)`) → `world_size = "small"` →
`background_node_range = {0, 1}` → `background_room = "BGImpassable"` so ocean
eats the margins → trim `contents.distributeprefabs` densities.

## After changing worldgen

1. Host a fresh world and confirm it generates at all.
2. Regenerate 3–5 seeds. Lock-chain mistakes show up as *randomly shaped* maps,
   not errors.
3. Grep the server log for `moderror` (dropped duplicate task/room definitions)
   and for the `[Story Gen]` lines, which name the starting task and the linker.
4. Ocean setpiece placement also fails silently — `Ocean_PlaceSetPieces`
   (`map/ocean_gen.lua`) prints only a total like
   `[Ocean] Placed 4 of 6 ocean set pieces.` and never says which it dropped.
5. For a cave shard, check the master log for
   `Skipping portal[1] (no available shard [Caves] connected)` — that means
   cave worldgen crashed, usually an invalid override value.
6. Confirm the result in the save, not just the log: `scripts/save_inspect.lua`
   counts prefabs, gives positions and tiles, and dumps topology
   (`references/save-files.md`).

## Part 3 — boss chains in a trimmed world

Several bosses are unlocked by runtime chains rather than placed by worldgen, so
cutting tasks or writing custom setpieces can break them with no error at all —
the break shows up days into a playthrough. `references/boss-prerequisites.md`
has the full chains with citations. The traps found so far:

- **Celestial Champion — altar geometry.** The three altars are socketed into
  `moon_fissure`s and only link if all are within 20 units, pairwise ≥ 2.5 apart
  and not near-collinear. A custom layout that places fissures can dead-end the
  whole chain.
- **Celestial Champion — moon storm hang.** `StartMoonstorm` loops until it finds
  4 connected land nodes that are neither `lunacyarea` nor `sandstorm`, with no
  retry cap. A world with eligible nodes but no such cluster hangs the server
  when the altars link.
- **Celestial Champion — the crown.** Crab King only drops `moon_altar_crown` if
  it was socketed with Pearl's pearl, so Pearl's island and her house materials
  (marble, moon rock, cookie cutters) are part of the chain.
- **Ancient Fuelweaver — Fossil Fragments.** 8 are needed. Without spider-hole
  rooms, stalagmites give one 10% roll each and do not regrow;
  `wormattacks = "never"` also removes the worm boss's drop.
- **Cross-shard bosses.** Scrappy Werepig (surface junk yard) only appears after
  the cave Nightmare Werepig is defeated — check
  `shard_network.persistdata.shard_daywalkerspawner.location` in the save.


