At a glance

What it is
A folder of TOML files (or one .lxpack file) the engine loads next to your disc. Nothing on the disc changes.
What it can add
Monsters derived from retail ones, formations with waves, bosses, encounter overrides, an XP curve and enemy scaling, quests, challenges, dialogue, trading cards and dealers, poker / Grid Duel / golf / racing / prop-hunt instances, custom maps with NPCs, props and entrances.
Where it plays
The native port (launcher or play-window --expansion) and the browser play page.
How it is made
By hand in any text editor, or on the expansion workbench in the browser.
Without a pack
The game as it shipped, bit for bit: every pack hook is the identity when no pack is loaded.
Example
Sample Expansion Pack, written with exactly the format and tooling any modder uses.

A pack is not a ROM patch

The project has two ways to change the game, and they point opposite ways. The randomizer and disc patcher rewrite bytes on your copy of the disc, so any emulator or console plays the result. An expansion pack leaves the disc alone and adds to the port: it can do what no disc edit can - more enemies than a battle has seats, a map that exists on no disc, a poker table - because the engine is the one reading it.

ROM patchExpansion pack
ChangesYour .bin (or a .ppf beside it)Nothing on the disc
Plays onAny emulator or consoleThe port, native or browser
Bound byRetail's code, sector sizes, free spaceThe engine's own limits
Shared asA seed or a PPFA pack folder or one .lxpack
Turned off byUsing the unpatched discUnload, or not loading it

Both share one rule: nothing that ships contains the disc's bytes.

Coordinates, not copies

A pack monster is always derived from a retail one: the pack writes base = { kind = "monster", id = 109 } and new numbers, and the engine reads that monster's mesh, textures, animations and attacks off your disc. A pack map names the retail scene whose scenery it reuses; an NPC names the retail placement whose model it wears; a card's portrait names the monster it shows. Each of those is a DiscRef, a coordinate into the disc.

the pack (shared) battle.tomlmonster id 109 maps/tower.tomlscene town01 + tiles maps: [[npcs]]npc town01 slot 12 maps: [[props]]tmd PROT 874 object 3 battle: bossesbgm 30 DiscResolver range check, then look up on the disc and read the asset your disc (never shared) PROT 867monster archive scene bundleVRAM, geometry pack scene MANactor placements PROT 874TMD objects music bankSEQ + VAB
Pack to coordinate to disc. The validator range-checks a coordinate with no disc at all; with a disc (in the engine, or on the workbench) it also checks that the coordinate names something real.

The validator backs the rule up: a file under assets/ must be a PNG, glTF, Ogg, WAV or plain text file and must not look like any disc format, and no file in a pack may be named like a disc file (the no-disc-bytes rule). That is what makes a pack safe to post anywhere.

What is in a pack

Every file but the manifest is optional, and every key in a file is optional: a pack writes only what it changes.

requiredpack.tomlid, name, version
fightsbattle.toml progression.tomlmonsters, curve
storyquests.toml dialog.toml cutscenes.tomlquests, lines, scenes
playcards.toml modes.tomlcards, modes
placesmaps/*.tomlone per map
own filesassets/png, ogg, ...

The pack as a directory, by what each file is for; as an .lxpack the same files sit in one deterministic archive.

How a pack reaches the engine

Both hosts end in the same call. The native window takes a pack folder or an .lxpack; the browser page takes an .lxpack (a file pick, or the bundle the workbench hands over). Each validates it - a pack with errors is refused, with the first few named - and activates it through SceneHost::activate_expansion_pack, which is World::activate_expansion: the battle tables compile, the XP curve swaps, the quest runtime adopts any progress a save kept for the pack.

native window Launcher buttonfolder or .lxpack --expansion PATH[--map ID] browser play page Load expansion.lxpack file input Workbench hand-offplay.html?expansion=1 load_expansionread_bundle validate, then activate_expansion_pack World::activate_expansion Plays with the pack New Game, Continue entrances to maps Journal: quests, cards Travel page --map / scene list pack fights, modes saves keep progress
Two hosts, one activation path. A save made with the pack keeps its progress in its own block, so loading it without the pack keeps that progress for later rather than losing it.

What a pack can do that retail cannot

  • Fights past the seat limit. A battle has four monster seats; a formation's waves bring more in as seats free up, and one victory pays for all of them.
  • A reason to reach 99. A banded or explicit XP curve, EXP and gold multipliers, and enemies that scale with the party past a level.
  • New places. A map painted tile by tile over a retail scene's scenery, with its own walls, heights, props, NPCs and encounter zones, reached through an entrance in the retail world.
  • New things to do. Quests that track retail story flags and the pack's own, challenges with scores, trading cards dropped in battle and sold at dealers, and five game modes - several of them playable online.

Start with the sample pack: load it, play it, then open it on the workbench and see how each piece is written.