Team Cereustooling wiki

Put a seed in the mod

Partly checkedagainst the code on 2026-11-12 11:45

Status partial. Read this first:

  • A seed from seedgen cannot be put in the game today. No tool turns its exported rows into a regulation with new pattern ids. seedgen's README lists what is missing: append the rows under new pattern ids, add MapPatternSet rows or a DLL pick, give each row the right patternSetId, and test in game.
  • The seeds Eclipse ships are planned by hand. Each one is a vanilla pattern plus a table of port placements. The castle-variants seed planner turns that into regulation rows, maps and events. That route is described below.
  • The commands and file formats below were read from the code. The full build was not run for this page. The parts that come from team notes and could not be confirmed in code are marked as notes with their date.

The pieces

Piece Where Job
Seed config S:/souls-script-kt/scripts/tools/castle-variants/config/ Which seeds exist and what is placed where.
seeds.py same folder Lists seeds, checks them against each other, helps pick a pattern and clean tiles.
plan.py same folder Config to a plan: rowlist.tsv (regulation rows), edits.json (map and event edits), plan.json.
CastleVariants.exe built from S:/eclipse-build/tools/dotnet/CastleVariants Applies edits.json to the MSB and EMEVD files.
build.py same folder as plan.py Runs the whole chain: plan, maps, regulation, port files, shared binders, checks, reports.
[seed] pool the release toml beside the Eclipse DLL Tells the game which pattern each Nightlord gets.

The config files

All under config/ in the castle-variants folder.

  • seeds/seeds.tsv: the seeds, one line each: pattern, name, status (live, next, idea), note. Plan order is file order.
  • seeds/<pattern>/slots.tsv: that seed's placements. Seed 240 still uses config/slots.tsv. Columns: slot (attach point, or night1 / night2), class, map, part (MSB part name), op (swap, clone:dx,dz, drop), port (a key of ports.tsv), reward, think, bgm, note, opts. The comment block at the top of config/slots.tsv explains every opts key. A port written NEW:<role> is a placeholder: the planner skips it and the vanilla part stays.
  • seeds/<pattern>/tiles.tsv (optional): attach, map, variation, copy, note. Makes the seed's own row load another vanilla tile or variation at that attach point, so the seed edits a tile no other seed shows.
  • seeds/<pattern>/night.tsv (optional): the seed's Day 1 and Day 2 night boss swaps (LotResultPlayAreaParam).
  • ports.tsv: every port, shared by all seeds.
  • poi-variants.tsv: every enemy set a POI tile can show. A POI port set is an added variation of the tile, never an edit of a vanilla one. A slot line names it with variant=<name> in opts. plan.py fails a POI line that names no variant, unless the registry lists that tile as in-place for that seed.

Steps

  1. Read the seed rules in Reference. Check the fill with Stan before you build. He picks what goes where.

  2. Look at what exists:

    py -3.12 -I -B seeds.py list
    
  3. For a new seed, find the tiles it shares with other seeds. <plan dir>/vanilla-parts.json comes from an earlier plan.py run.

    py -3.12 -I -B seeds.py tiles <pattern> --parts <plan dir>/vanilla-parts.json
    py -3.12 -I -B seeds.py candidates --top 25 --parts <plan dir>/vanilla-parts.json
    

    tiles lists attach points that would show another seed's edited parts, with vanilla tiles and variations that show none. Put the picks in config/seeds/<pattern>/tiles.tsv.

  4. Add the seed to seeds/seeds.tsv and write its slots.tsv.

  5. Check the seeds against each other:

    py -3.12 -I -B seeds.py check --seed 240,1153,216,280
    

    It checks that a boss family sits in at most one seed and at most one boss slot, placeholders, Day 2 bosses, Nightlords and earths, and maps or parts two seeds both edit.

  6. Plan. All seeds are planned together, because the game loads one regulation and one map set:

    py -3.12 -I plan.py <out dir> [<mod out dir>] --seed 240,1153,216,280
    

    --seed all takes every line of seeds.tsv. Without --seed, only seed 240 is planned. --no-arena skips the Boss Arena feed files.

  7. Apply the map and event edits:

    CastleVariants.exe build apply <out dir>/edits.json
    
  8. Or run the whole chain with the driver, which does steps 6 and 7 and the rest:

    py -3.12 -I build.py <plan dir> <package dir> [--refresh-arena] [--skip-verify] --seed 240,1153,216,280
    

    Its steps, from the code: plan.py; CastleVariants build apply; spot post-steps; Polish apply of the vanilla regulation plus rowlist.tsv into <package>/mod/regulation.bin; package.py (port files); CastleVariants names and matbins; dumps; verify.py and the reports. --skip-verify stops before the last step.

  9. Tell the DLL about the seed. A run only gets an Eclipse seed when its Nightlord has an entry in [seed] pool of debug-nightreign.release.toml:

    [seed]
    pool = [
      { nightlord = 6, pattern = 240 },
      { nightlord = 9, pattern = 1153 },
    ]
    

    One entry per Nightlord and per pattern. don_weight (default 1) is that Nightlord's weight in Deep of Night; 0 keeps it out of Deep of Night. See [seed] in the debug DLL reference.

  10. Stage it as a release stage and run the release checks. That is the release pipeline's job, not this page's.

The recipe that works on recent bases (note, 2026-11-10)

From the team memory eclipse-seed-planner-recipe. The env variables exist in lib.py and plan.py; the rest is the note's claim and was not re-run.

  • Set CV_ARENA_BASE to the 0.0.51 release regulation: S:/souls-script-kt/output/eclipse-release-0.0.51/depth6-online-mods/mod/regulation.bin. With it, plan.py --seed 240,1153,216,280 runs clean. Without it the run stops on a port-rows file whose copy sources are missing in vanilla; lib.py prints the hint set CV_ARENA_BASE for that case. A 0.0.9x regulation as base still fails.
  • Run a frozen copy of the castle-variants folder inside the stage's work/, through the build scheduler: eb.py run --mem 2 -- <command>.
  • Build CastleVariants from source: eb.py tool build castlevariants_src. tools.toml says why: the frozen 0.0.86 binary lacks ops the source has (ungroup and others).
  • Never ship a from-vanilla rebuild of a map that a later stage changed. A rebuild from vanilla does not reproduce the published files for 14 maps. Start from the published files and add to them with CastleVariants build apply.
  • Night arenas have no per-pattern variation. A night boss swap changes that arena for every pattern that uses it.

Other things plan.py reads from the environment

Variable Meaning
CV_ARENA_BASE A release regulation.bin used as the base of the port rows; its rows win.
CV_MASTER_RELEASE Overrides which release's master rows are used; - turns that off.
CV_DUMP_OVERLAY ; list of dump folders searched first; the first one receives new dumps. Without it, dumps go to <out dir>/dump.
CV_ARENA, CV_ARENA_OVERLAYS Which Boss Arena package the port files and rows come from.
CV_SRC_EVD Folder of the source EMEVD files (default S:/souls-script-kt/.tmp/castle/nr/event).
CV_ARENA_PARITY verify.py: warn or off relaxes the Boss Arena check.

How to tell it worked

  • plan.py and seeds.py check end without FAIL lines.
  • build.py exits 0; verify.py writes its report under <package dir>/reports.
  • In game, a run against a pool Nightlord shows the planned map. seed-run-log.jsonl beside the DLL has one line per expedition with rolled, forced and committed. Stan starts the game; never launch it yourself.

What goes wrong

  • A second seed shows the first seed's bosses. Field boss tiles and evergaol parts are shared by most patterns. Use tiles.tsv or added variations. Note (2026-11-06): every launch-era field attach point (750 to 763) has exactly one tile, and seeds 240 and 1153 edit them in place.
  • plan.py fails a POI line: the line edits a POI tile without variant=. Add the variant to poi-variants.tsv.
  • A Nightlord whose pattern belongs to another Nightlord has hung loads before. The pool entry's Nightlord must be the pattern's own (targetBoss).
  • plan.py is heavy. Run it through eb.py run --mem, and not while Nightreign is open.
Source files this page documents (11)
  • sskt:scripts/tools/castle-variants/plan.py
  • sskt:scripts/tools/castle-variants/build.py
  • sskt:scripts/tools/castle-variants/seeds.py
  • sskt:scripts/tools/castle-variants/poi_variants.py
  • sskt:scripts/tools/castle-variants/lib.py
  • sskt:scripts/tools/castle-variants/config/seeds/seeds.tsv
  • eb:tools/dotnet/CastleVariants/Program.cs
  • eb:tools/dotnet/CastleVariants/Build.cs
  • eb:tools/tools.toml
  • fsrs:tools/debug-nightreign/src/seed_pick.rs
  • fsrs:tools/debug-nightreign/src/config.rs

Changed one of these? Update this page: seeds/put-a-seed-in-the-mod.md (how).