Scenario Stats & Completion
Record player choices as stats, show them on an end screen, and mark the scenario complete when the narrative finishes.
Overview
- Define labelable stats in
src/stats.json - Emit values from Ink with
#stat:id:valuewhen choices resolve - End the scenario with
#completeScenario
The player app queues stats offline, syncs them when signed in, and opens a completion screen that shows the player's recorded choices using your labels from stats.json.
stats.json
Place this file next to scenario.json in your scenario source (usually src/stats.json). It is optional — without it, the end screen still works but falls back to raw ids/values.
{
"stats": [
{
"id": "who_you_told",
"label": "You told the group",
"buckets": [
{
"value": "found_phone",
"label": "You found Sarah's phone"
},
{
"value": "call_police",
"label": "You wanted to call the police"
},
{
"value": "ask_location",
"label": "You asked where Sarah was last seen"
}
]
},
{
"id": "group_plan",
"label": "The group decided to",
"buckets": [
{
"value": "search_together",
"label": "Meet up and search at Club Neon"
},
{
"value": "discuss_concerns",
"label": "Talk through Sarah's recent worries"
},
{
"value": "contact_friends",
"label": "Contact her work friends immediately"
}
]
}
]
}Fields
| Field | Required | Description |
|---|---|---|
stats[].id | Yes | Stable id used in #stat:id:value |
stats[].label | Yes | Human-readable question / category on the end screen |
stats[].buckets | No | Allowed values and display labels |
buckets[].value | Yes (if buckets) | Value string passed to #stat: |
buckets[].label | Yes (if buckets) | Text shown for that value |
Keep ids stable
Once players have recorded a stat_id / value, changing those strings breaks label lookup for existing sessions. Prefer updating label text instead of renaming ids.
Ink tags
#stat:id:value
Records a play-through choice.
== ending_search_together ==
{
- who_you_told == "found_phone":
# stat:who_you_told:found_phone
-> emit_search_together
- who_you_told == "ask_location":
# stat:who_you_told:ask_location
-> emit_search_together
}
== emit_search_together ==
# stat:group_plan:search_together
-> finish_scenarioRules:
- Format is exactly
stat:<id>:<value>(two colons separating three parts) idandvaluemust be non-empty- Prefer values that match a bucket in
stats.json - Emitting the same
idagain overwrites the previous value for that session (latest wins on the end screen) - Safe to put on its own line with no message text
#completeScenario
Marks the scenario finished: flushes queued reporting events, completes the remote play session when available, and opens the completion UI.
== finish_scenario ==
Thanks for playing. #system #delay:1s
# completeScenario
-> ENDWhen to use: Once, at the true ending knot(s). Call it after you have emitted any final #stat: tags so they appear on the end screen.
Don't skip END
Still divert to -> END (or otherwise stop the conversation) after #completeScenario. The tag handles reporting/UI; it does not replace Ink flow control.
Legacy aliases
Older packs may still use #completeThread or #completeStory. The player app still accepts those tags.
Suggested pattern
- Track decisions with Ink variables as the player chooses
- At each ending branch, emit one or more
#stat:tags that summarize the path - Divert to a shared
finish_scenarioknot that shows a short farewell and#completeScenario
VAR who_you_told = ""
== player_found_phone_response ==
~ who_you_told = "found_phone"
// ... dialogue ...
-> group_follow_up_choices
== ending_call_police ==
# stat:who_you_told:call_police
-> finish_scenario
== finish_scenario ==
Thanks for playing the sample scenario. #system #delay:1s
# completeScenario
-> ENDWhat players see
- A scenario complete banner / screen
- Labeled stats from
stats.jsonfor values recorded this session - Options to continue exploring the phone OS or leave the scenario (player app behavior)
Stats sync to the server when the player is signed in and play-session reporting is enabled. Offline play still records locally so the end screen works without a network.
Checklist
- [ ]
src/stats.jsonlists everyid/valueyou emit - [ ] Every ending path emits the stats you care about
- [ ] Exactly one
#completeScenarioper finished playthrough (shared finish knot is ideal) - [ ] Rebuild and republish after changing
stats.jsonor Ink tags
Related
- Tag Reference — full tag list including
#toast:and#stat: - Ink Scripting Guide — variables, branches, endings
- Project Structure — where
stats.jsonlives - Build and Publish — package and upload your scenario