Skip to content

Tutorial Modern Individuals

johnjohto edited this page Jul 28, 2026 · 4 revisions

Tutorial: Modern Individuals (mon/2)

Give every monster a nature, ability, held item, IVs, EVs, gender, and a shiny flag -- as an opt-in wire upgrade that migrates existing saves deterministically.

1. Declare the wire in data/ruleset.json: {"mon_wire": 2}

2. Provide natures -- data/natures.json:

{
 "natures": ["nature:bold", "nature:brave", "nature:calm"],
 "effects": {"nature:brave": {"up": "atk", "down": "spd"}}
}

(mon_wire: 2 without this file refuses at boot -- the wire names natures, so it needs the vocabulary. The modern recipe imports all 25 for you.)

3. That's it. Every existing save backfills on load -- one loud line reports the count -- and every field derives deterministically from the monster's own DNA: IVs from its DVs, gender from the species ratio, shininess from the classic DV pattern, its nature from a stable hash. Two loads agree; two link partners agree. Trades refuse cleanly at hello if the other copy speaks a different wire or nature table, instead of desyncing mid-battle.

Species gain an optional gender_ratio byte (0 = all male ... 254 = all female, absent = genderless), moves an optional category (physical/special/status), and the ruleset config a shiny_odds knob. Nothing about battle math changes until a modern formulas provider engages -- declaring the wire alone is provably inert (that's a release gate).

Verify your stat math in-engine

If you bind stat kernels (stat_calc2 for the modern formula, stat_base_scale for a per-individual pre-scale), you can assert what the engine actually computes -- no inference, no reimplementation. Write a fixture:

{"species": "snorlax", "level": 50,
 "ivs": {"hp": 31, "atk": 31, "def": 31, "spd": 31, "spc": 31},
 "evs": {"hp": 252, "atk": 252}, "nature": "brave",
 "ext": {"grade": 4}}

and run:

python tools/run.py --project=path/to/yourgame --statread=fixture.json

Every input is pinned (absent fields take the pipeline's documented defaults: DV 0, IV = DV*2, EV 0, stat exp 0, neutral nature; a sexp bag pins Gen-1 stat experience for faithful-formula vectors), and the output is one line per stat -- base / prescale / final -- where prescale is your stat_base_scale's answer and final is the whole pipeline's. The same fixture always prints the same bytes, so a reference oracle's test vectors drop straight in as acceptance fixtures. A typo'd species, stat key, or nature, an ext field you never declared in mon_fields, or an ext value outside its declared schema fragment refuses loudly instead of computing something you didn't ask about.

Clone this wiki locally