Tutorial onboarding (Ember Reach) — design
Tutorial onboarding (Ember Reach) — design
Section titled “Tutorial onboarding (Ember Reach) — design”Status: brainstormed with Chris 2026-07-21. This is a deliberate v0.1 feature-freeze
exception (see docs/RELEASE-0.1.md — feature freeze is normally bug fixes/QA/docs/release
tasks only) — Chris wants new players taught the game properly before landing in Survival, and
approved re-enabling + extending the built-but-gated Story/Tutorial system to do it.
Every new player should be taught, in order: moving the ship (left stick), that they have both an auto-aiming weapon and a stronger manual weapon aimed with the right stick, how the dash/warp dodge works and how to read its readiness off the engine glow, and how to launch the decoy. Then they should land in Survival — the mode v0.1 actually ships as the main loop — without being walled behind the full campaign.
Context (what already exists — verified via code, not memory)
Section titled “Context (what already exists — verified via code, not memory)”Storyline mode (“Ascent: The Spectrum”) is not vaporware. It’s a fully built, tested campaign
system — StoryDirector, StoryData, StoryState, dialogue/region-title UI, NPCs, 4 elemental
domains (Ember → Hush → LiveWire → Null) each ending in a Warden boss — currently hidden from
the v0.1 menu by one flag:
ui/start_menu.gd’sconst V01_CRYSTALS_ONLY := truecollapses the 3-entry mode picker (tutorial/story/survival) down to Survival only. The other two modes still exist, still pass their tests, and are fully wired end-to-end (main.gd’s_modevar and_is_story()already branch on all three).- A dedicated
"tutorial"mode already exists (distinct from"story"): it replays the same Ember→Null campaign but pauses the sim during dialogue, uses a fixed seed (sim.enable_story(sdata, not is_tutorial, not is_tutorial)—suppress_tutorial=false,randomize=false), and currently has no stopping point — a player can walk straight from Ember into Hush and play the whole campaign under_mode == "tutorial". MetaState.tutorial_doneflips true oncestory.regions_seen.size() > 1 or story.complete(main.gd:1179-1180) — i.e. today it only flips on reaching region 2 or finishing the entire campaign. There is no “first act cleared” checkpoint.- In story mode,
active_weapon_idsstarts empty (sim.gd:965, “story starts weaponless”) butPilotArsenalalways instantiates the manualWeaponAimregardless of mode — every pilot has the right-stick weapon from the first tick. The player’s very first NPC interaction (echo_intro, roomember_entry) grants the auto-firing blade (give_bladereward) and its paired dialogue (ember_intro) already explains both weapon types: “Your weapons fight on their own — just fly, and they find their marks. The right stick throws an aimed bolt.” - The decoy is already taught:
echo_decoy(on_enterfor roomember_a2, the second arena) explains the meter-fueled, always-available decoy. This is not the same thing as the Drone Bay companion drone, which requires a gold-shop purchase (unlock-drone-<klass>) before it can ever be deployed — a fresh save owns none, so it can’t be the thing taught here. - Dash/warp has no teaching content at all today.
PlayerState.dash_cd(0 = ready) already drivesrender/player_renderer.gd’s thruster glow via adash_readyboolean (line 191) — the “glow tells you when it’s ready” visual the player asked for already exists in the renderer, it’s just never explained to the player. ui/control_hint.gd(ControlHint) is a platform-tailored (touch/stick/mouse), self-dismissing movement reminder. Correction from an earlier pass of this design: it is not dead code —main.gd’s_new_run()originally created it gatedif not _is_story()(commitc736b354, 2026-06-30 — i.e. it was only ever meant for Survival, since Tutorial/Story teach movement via dialogue), then Chris explicitly asked for it to be removed entirely (commitc7ef9d9, 2026-07-05, “per Chris’s request”).main.gd:908today is justcontrol_hint = nullwith a comment recording that removal. Verified via git history, not assumed.
Design
Section titled “Design”1. Menu & first-run flow
Section titled “1. Menu & first-run flow”- Narrow
V01_CRYSTALS_ONLY’s effect from “Survival only” to “Tutorial + Survival” (Story stays hidden — a bigger, separate commitment Chris explicitly deferred past v0.1). - Fresh save (
meta.tutorial_done == false): skip the menu picker entirely on first launch, start a Tutorial run directly. Returning players (tutorial_done == true) see the normal 2-entry menu; Tutorial stays selectable for a replay. - Tutorial is capped to Ember Reach only (the first of the 4 domains), not the full campaign. The instant the player defeats the Ember Warden, a short victory beat plays, then the run ends and a Survival run auto-launches — no menu stop in between, no hard lock (a player who wants to bail into Survival before finishing Ember can already do so via pause/quit, same as any run today; nothing about this design blocks that). The auto-launched Survival run starts exactly as it would from the menu — default ship/loadout, since this is necessarily the player’s first Survival run on a fresh save.
tutorial_donegets a new flip condition: Ember cleared while_mode == "tutorial", checked alongside (not replacing) the existing region/complete check, since the latter still matters for the separate"story"mode’ssuppress_tutorial/randomizebehavior once Story is re-exposed post-v0.1.
2. Teaching content
Section titled “2. Teaching content”Most of what’s needed is already-shipped dialogue; the actual gap is small.
| Mechanic | Status | Plan |
|---|---|---|
| Movement (left stick) | Untaught in Tutorial (Chris removed ControlHint everywhere 2026-07-05) |
Re-instantiate ControlHint, but only for Tutorial-mode runs — respects the 2026-07-05 Survival removal, which stays as-is. Shows before the player reaches Echo; reaching Echo to trigger ember_intro also requires the player to have moved regardless. |
| Auto weapon vs. manual weapon | Already taught (ember_intro) |
Keep as-is; light copy polish only if playtesting shows it’s unclear. |
| Dash/warp | Not taught anywhere | Add 1-2 new lines to the existing ember_intro speech (same beat that already covers weapons + healing) — explain the dash button, that it grants a moment of invulnerability, and to watch the engine glow for readiness. No new dialogue node or trigger — ember_intro already fires before the player’s first real fight (ember_a1), which is the right moment. |
| Decoy launch | Already taught (echo_decoy) |
Unchanged. |
3. Implementation touch points
Section titled “3. Implementation touch points”ui/start_menu.gd— narrow theV01_CRYSTALS_ONLYmode list to[tutorial, survival].main.gd— onmeta.tutorial_done == false, bypass_show_start_menu()and start a tutorial run directly.main.gd/sim/story_director.gd— new branch: when_mode == "tutorial"and the Ember Warden dies, override the normal “portal to Hush” transition (ember_portal’sember_outrodialogue currently promises “The Hush is next”) with a short tutorial-specific victory beat, then end the run and auto-launch Survival.main.gd— newtutorial_doneflip condition for the capped-tutorial case.main.gd— re-instantiateControlHintat run start, gated to Tutorial-mode runs only (Survival’s 2026-07-05 removal is untouched). Verify it doesn’t visually clash withember_intro’s dialogue box, which fires shortly after (should be fine — the hint is only on screen before the player reaches Echo).data/story.json— add the dash/warp lines toember_intro.
4. Testing
Section titled “4. Testing”- Extend
tests/test_story_*.gdfor the new capped-tutorial→Survival handoff and the newtutorial_doneflip condition. - This is all
story != null/ render-only territory (ControlHintis pure UI, no sim state) — the Survival determinism baseline is untouched, no re-pin expected. - Manual playtest on Mac (fresh save → forced tutorial → Ember Warden → auto-Survival → subsequent launch shows the normal 2-mode menu) before an Apple TV device deploy, per the project’s existing on-device verification convention.
Out of scope (explicitly deferred)
Section titled “Out of scope (explicitly deferred)”- Re-exposing the full 4-domain Story campaign on the v0.1 menu.
- Teaching the Drone Bay companion drone (requires a shop purchase a fresh save can’t have).
- Any new dash/warp mechanic — this uses the existing dash exactly as implemented today, just narrated for the first time.
- Reactive, just-in-time teaching triggers (e.g. “you took a hit you could’ve dashed away from”)
— the design uses the existing scripted
on_enter/NPC-interaction dialogue pattern instead, matching how every other lesson in Ember Reach already works.