-
Notifications
You must be signed in to change notification settings - Fork 1
Tutorial Per Save Rulesets
One project, two stat models. data/ruleset.json's rulesets map declares named variants over your root declaration; a save binds one of them — in-story, exactly once — and keeps it for life. "Faithful mode" and "modern mode" stop being two projects.
Your root ruleset.json stays the faithful default (every save starts there). Each named entry may declare any of formula_scripts, config, and stats:
{
"base": "gen1",
"mon_fields": {"era": {"type": "string"}},
"rulesets": {
"modern_mode": {
"formula_scripts": {
"stat_calc2": "script:two_mode_stats",
"ev_gain": "script:ev_caps"
}
}
}
}A key an entry declares replaces the root's wholesale; a key it omits inherits the root. What's bound is always exactly what you can read on the page — no merging, no conditions. (when-conditioned bindings are deliberately rejected: a binding that flickers with a flag can't be replayed or refused.)
stats is the ordered stat vocabulary — today the engine runs exactly ["hp","atk","def","spd","spc"], so declaring it is optional; the six-stat era will widen it here, per variant.
The identity-shaping keys — base, mon_fields, mon_wire, battle_hooks — stay root-only: every save of your project speaks the same wire.
The mode choice is a narrative moment. A new event command binds the save:
{"cmd": "ask", "text": "Which world calls to you?",
"options": ["The old ways", "The new ways"], "result": "world_choice"},
{"cmd": "if", "cond": "world_choice == 1",
"then": [{"cmd": "set_ruleset", "name": "modern_mode"}]},
{"cmd": "give_mon", "species": "species:sproutle", "level": 5}Choose "the old ways" and you simply never bind — the save stays on the faithful root.
Three rules, all loud:
-
Once. A second
set_rulesetrefuses and aborts the event. - Before the first owned individual. Any mon already in the party, a box (including box banks and shared storage), the Day Care, or an in-flight trade makes the bind refuse — individuals must never predate the rules that made them.
-
Declared names only. An unknown name refuses at
--validateand at load, never mid-play. Every declared variant is also checked at boot (kernels, scripts, stats) even if no save ever binds it.
Because the engine grants creator projects a stopgap starter at new game, set "starter_level": 0 in presentation/version.json and give the first mon yourself in the start event — after the choice.
The binding rides the save file as one additive key ("ruleset": "modern_mode"); unbound saves are byte-identical to before. Loading re-binds first, so every kernel and knob already stands on the save's rules before anything else happens.
Linking: the binding joins the handshake identity. Two saves on different rulesets — or the same name with edited kernel scripts — refuse to link with a message naming the mismatch, instead of desyncing a lockstep battle. Same binding, same math: peers agree by construction.
Pairs with Modern Individuals (the ext marker + widened stat_calc2 two-mode kernel) and The Modern Ruleset. Gate: --rulesetbindtest.
Monworks: MIT code · CC0 samples · everything pictured is original content. The faithful Pokémon Red build derives from your own local pokered clone and is never distributed. Pokémon is © Nintendo/Creatures/GAME FREAK; this is an unaffiliated fan engineering project.
Play
Create
Tutorials
- Writing Text
- Battle Backdrops
- Colors and Palettes
- Display Filters and Shaders
- Widescreen and Scaling
- Quality-of-Life Knobs
- Overlays (Recipes)
- Abilities & Battle Hooks
- Modern Individuals
- Per-Individual Fields
- Item Field Scripts
- Field Effects
- The Modern Ruleset
- Per-Save Rulesets
- Six Stats
- The Clock
- Conditioned Bindings
- Day and Night
- Sprite Variants
- Overworld Spawns
- Language Packs
- Set-Piece Battles & Narration
- Run a Server
- Join a Server
Elsewhere