moymo.toml
moymo.toml lives next to your project and says what the game tells Moymo. moymo push checks it, writes the typed code your game calls, and uploads it. Commit it together with moymo.lock.
This page covers what Moymo reads today. A table that is planned but not built yet ([journey], [boards], [party], [saves], [pulse], [reach], [prefs], [live], [mods], [recap], [bridges]) gives a warning and is ignored. Any other unknown table is an error.
An example
moymo.toml
toml[game]
id = "starfall-outpost" # the app key from the Developer Portal
name = "Starfall Outpost"
locales = ["en", "cs"] # the first is used where a player's language is missing
modules = ["moments", "presence", "achievements"]
[codegen]
cpp = "Source/StarfallOutpost/Moymo.g.h" # or csharp = "Assets/Moymo/Moymo.g.cs"
[lists]
paths = ["agrarian", "industrial", "martial"]
[labels.paths] # needed only where people read the symbols (presence)
agrarian = { en = "farming", cs = "zemědělská" }
industrial = { en = "crafts", cs = "řemeslná" }
martial = { en = "arms", cs = "vojenská" }
[events.raid_repelled]
fields = { wave = "int(1..)", losses = "int(0..)", minutes = "float" }
[events.path_chosen]
fields = { path = "enum(@paths)" }
[moments.raid_repelled]
on = "raid_repelled"
importance = "epic"
clip = { before = "25s", after = "4s" }
title = { en = "Raid repelled · wave {wave}", cs = "Nájezd odražen · vlna {wave}" }
[presence.lines.defending]
text = { en = "Defending the settlement · wave {wave}", cs = "Brání osadu · vlna {wave}" }
params = { wave = "int(1..)" }
[presence.lines.on_path]
text = { en = "On the {path} path", cs = "Na cestě: {path}" }
params = { path = "enum(@paths)" }
[stats.raids_repelled]
kind = "counter"
from = "raid_repelled"
[stats.population]
kind = "max" # no `from`: the game puts it
[achievements.night_held]
name = { en = "Not a Scratch", cs = "Ani škrábnutí" }
description = { en = "Repel a third-wave raid without losing anyone.", cs = "Odraz třetí vlnu bez ztrát." }
on = "raid_repelled"
when = "event.losses == 0 && event.wave >= 3"
steam = "ACH_NOT_A_SCRATCH"
[achievements.full_household]
name = { en = "A Full Household", cs = "Plný dvůr" }
description = { en = "Have 20 people living in your settlement.", cs = "Měj v osadě 20 obyvatel." }
rule = "stats.population >= 20"Names and text
- Keys (
raid_repelled,night_held) arelower_snake_case, at most 64 characters, starting with a letter. The generated code turns them intoRaidRepelled,NightHeld. - Text is either one string for every language (
title = "Raid {wave}") or one per language ({ en = "...", cs = "..." }). Every language ingame.localesneeds its text.{wave}puts in a field of the event (moments) or a parameter (presence lines).
[game]
| Key | |
|---|---|
id | The app key the Developer Portal gave the game. Lower case, digits, - and _. |
name | 1 to 100 characters. |
locales | The languages you write text in. The first is the fallback. |
modules | What the game uses: moments, presence, achievements. These are also what Moymo for Windows asks the player to allow. |
[codegen]
| Key | |
|---|---|
csharp | The C# file to write (Unity), relative to moymo.toml, for example Assets/Moymo/Moymo.g.cs. |
cpp | The C++ header to write (Unreal Engine, any C++ engine), for example Source/MyGame/Moymo.g.h. |
namespace | Default Moymo.Game (C#) or moymo::game (C++). Write it with dots: MyGame.Moymo. |
Both paths stay inside the project: no .. and no absolute paths.
[lists] and [labels]
A list is a named set of symbols that fields and rules share: paths = ["agrarian", "martial"]. A field uses it as enum(@paths). A rule reads it as lists.paths.
[labels.<list>] gives each symbol a text, in every language. A presence line can show a list's symbol only if every symbol has a label: a friend reads farming, not agrarian.
[events.<key>]
Something that happens in the game. Moments, stats and achievements follow events, so the game usually only emits.
| Key | |
|---|---|
fields | Up to 8 fields: int, a range such as int(1..), int(0..10) or int(..5), float, bool, enum(walls, gate), or enum(@list). |
analytics | true marks the event as analytics. |
[moments.<key>]
A big moment. Moymo for Windows keeps a clip of it if the player records clips, and names the clip by its title. Needs moments in game.modules.
| Key | |
|---|---|
on | The event that marks it. Without on, the game marks it from code. |
importance | minor, notable (the default), major or epic. |
clip | How much to keep before and after: { before = "25s", after = "4s" }. Default 10 s before, 3 s after. At most 120 s before and 60 s after. |
title | Text. It can use the event's fields: {wave}. |
[presence.lines.<key>]
A line under the player's name that friends read, in the friend's own language. Needs presence in game.modules.
| Key | |
|---|---|
text | Text with {param} placeholders. |
params | Whole numbers (int, int(1..)), or enum(@list) of a list with a label for every symbol. Never free text: a friend only ever reads what you wrote here. |
[stats.<key>]
A number or a set the game keeps per player. Rules read stats to unlock achievements. Stats are kept on the player's PC; Moymo does not merge them across PCs yet.
kind | Keeps | From an event (from) |
|---|---|---|
counter | A total that only grows. | Adds add (default 1) for each event. |
max / min | The highest or lowest value seen. | value, for example event.wave or event.minutes * 60.0. |
set | Distinct symbols, such as the kinds of building built. | value, for example event.kind. |
flag | Becomes true and stays true. | value (a condition), or true for any event. |
from: the event that feeds the stat. Without it, the game puts the value itself (Stats.Putin C#,moymo::Stats::PutIntand the rest in C++).when: a condition on the event. Only matching events count.type:int(the default) orfloat, for a counter, a max or a min.
[achievements.<key>]
Shown on the player's Moymo profile once unlocked. Needs achievements in game.modules.
| Key | |
|---|---|
name, description | Text. |
hidden | true: the name stays hidden until unlocked. |
steam | Its API name in Steamworks. The SDK unlocks it on Steam too (Unity and C++; see your engine's page). |
How it unlocks. Pick one way; the code can always unlock it too:
| (nothing) | Only the game unlocks it: Achievements.Unlock(Ach.FirstBanner). |
on | An event unlocks it. Add when for a condition on the event's fields. |
rule | A condition on stats, checked whenever they change: stats.population >= 20. |
requires | Other achievements that must all be unlocked first: requires = ["night_held", "full_household"]. It can be combined with rule. |
An unlock is never taken back, even if you change a rule later. A manifest that adds a rule the player's stats already meet unlocks it the next time the game starts.
Conditions (CEL)
when, add, value and rule are short expressions in CEL. moymo check runs each one on sample values and says what is wrong before you push.
| Where | It can read |
|---|---|
when, add, value | event.<field>, lists.<list> |
rule | stats.<stat>, lists.<list>, achievements (the keys already unlocked) |
text
event.losses == 0 && event.wave >= 3
stats.raids_repelled >= 10
lists.paths.all(p, p in stats.paths_taken)
size(achievements) >= 5
'night_held' in achievements && stats.population >= 50moymo.lock
moymo push (and moymo codegen) gives every event, moment, presence line, achievement and stat a number the first time it sees it, and writes it to moymo.lock. Commit that file. A number is never reused, so a build made today keeps working after you add more. To rename a key, add a new one; a typo in a name or a description can be fixed freely.
Limits
256 events, 128 moments, 64 presence lines, 512 stats and 1000 achievements per game. Each event has at most 8 fields.