Dev Log: 2026-05-31¶
Branch: rc/v0.1.3-dev
The tutorial system¶
This entry documents the tutorial system as it stands today: what it is, why it is designed the way it is, and how the pieces fit together.
The design challenge¶
Panzer Island's core mechanic, the reactive turn system, is not self-evident from looking at a screenshot. Nothing moves until the player acts. There is no turn counter, no phase banner, no blinking "GO." Drones sit still until a unit steps or fires, then each one reacts in sequence before the player gets control again. For players with strategy-game experience this clicks quickly; for everyone else, the first few minutes are spent wondering why nothing is happening, followed by wondering why everything happened at once.
The usual answer is a tutorial pop-up. Pop-ups have a well-known failure mode: players click through them as fast as possible to get to the game. The text explains a mechanic the player has not yet felt, the player ignores it, and then they feel the mechanic for the first time with no guidance at all.
The second usual answer is locking the player to a scripted sequence: tap here, now tap here, congratulations you moved. This solves the click-through problem but creates a new one: the player performs the gesture without engaging with the decision, and the moment the tutorial releases control they are no more prepared than before.
What I wanted was guidance that drives a real game action inside a real stage, teaches the smallest useful unit of information per step, and gets out of the way the moment the lesson is demonstrated.
Two tiers¶
The system ended up with two distinct tiers that serve different purposes.
Tier 1, the passive archive, is handled by TutorialManager. It tracks
which stages the player has entered, unlocks a browsable "help" archive entry
for each one, and shows a one-shot pop-up with the stage's tutorial_text on
first entry. The pop-up fires once per stage, never again on retries. The
archive is accessible any time from the HUD "?" button. This tier handles
Chapter 2 and later, where the stage text stays in the archive but no forced
pop-up interrupts the player. It also owns the Stage 0 generic "how to play"
card, which is always visible in the archive.
Tier 2, the invasive guided system, is the main subject of this entry. It handles Chapter 1 Normal only and replays at the start of every sector, every time the player enters. Where the passive tier is opt-out via "I've already read this," the invasive tier is opt-out via an explicit skip. The two tiers do not overlap: when an invasive script covers a sector, the passive pop-up is suppressed for that sector. The archive unlock still fires so the "?" button stays populated.
Architecture¶
The invasive system has four components.
TutorialDirector is an autoload. Its only public entry point is
run_for_layer_async(sc, stage_num, layer_idx), called once per sector entry
after any cutscene and objective banner have fully resolved. It checks whether
the current context is eligible, loads the script for the stage, and hands off
to TutorialRunner. It owns one transient flag, "skip all this launch," which
survives sector transitions within one stage session but resets at the next
launch. Chapter eligibility is hard-coded to 1-3 for now.
TutorialRunner is a per-sector Node, added as a child of StageController
for the duration of one sector's tutorial. It walks the script's segments and
steps, applies the input gate for each step, and awaits the correct completion
signal before advancing. When the last step resolves, it tears itself down and
returns control to the stage.
CoachOverlay is a CanvasLayer at layer 19, above the HUD and below pause
modals. It renders the spotlight (four dimming ColorRect strips framing a
cutout over the target), a pulsing blue ring on the cutout, a one-word TAP or
PRESS chip beside the ring, an Erika coachmark bubble with the step text, a
per-segment skip glyph inside the bubble, and a persistent "Skip tutorial" pill
in the top-right corner of the board. All cosmetic elements use
MOUSE_FILTER_IGNORE; the bubble, skip glyph, and skip pill are interactive and
added last so they always hit-test above the full-screen tap catcher beneath
them.
TutorialRefResolver is a static helper that resolves entity references against
the live stage state. A ref is a tagged dictionary, for example
{"unit": "katyusha"} or {"drone_tag": "tower_back"} or
{"hud": "limit_gauge:katyusha"}. Resolution is deferred to step execution
time so mobile drones that drifted since the script was authored still resolve
correctly. If a ref cannot resolve (the unit died, the drone was already
destroyed), the step's visual is suppressed and the runner either keeps a
still-meaningful await or advances automatically.
The script format¶
Tutorial scripts live in data/tutorials/ch<NN>/stage_NN.json. The file stores
only translation keys, never literal strings, so the JSON is locale-agnostic and
adding a locale means adding a CSV row, not touching the script. Every step's
text_key resolves through tr() at runtime.
A script is a list of segments. A segment is the skippable unit and contains an ordered list of steps. The step types in the shipped implementation are:
message: coachmark appears near the anchor; the board is tap-swallowed while the step is active; advances when the player taps the coachmark (or anywhere, after a short debounce).await_tap: the anchor is spotlit and the input gate allows only the target cell; other taps produce a nudge ("Not there. Tap the glowing tile."); advances on a valid tap.await_action: similar gate, but advances only after a full action resolves, including all drone reactions.free_move: truecan be set when the player needs to reposition first (the gate is cleared so they can move freely, but the step does not advance until an action settles).await_inspect: spotlights a drone and advances on a long-press inspect. Plain taps are swallowed with a long-press nudge. Atimeout_msbackstop prevents anyone from being trapped if the gesture is not reachable on their device.free_play: clears the gate and hands the rest of the sector to the player. An optional text line shows briefly, then fades. No further steps run.
The rule is: demonstrate the mechanic once, then hand off. No segment holds the player to the end of the stage.
Input gating¶
The gate is a two-layer design. StageController holds a _tutorial_gate
array of allowed cells and a one-line guard at the top of _handle_click. When
the gate is active and the tapped cell is not in the allowed list, the tap emits
tutorial_tap_rejected and returns; otherwise it emits tutorial_tap_accepted
and falls through to the real selection and routing pipeline. This means a valid
guided tap drives a genuine game action through the same code path as any other
player tap, including drone reactions, XP, and level-up dialogs.
The input block (push_input_block / pop_input_block) stays raised for the
entire guided segment. This suppresses camera drag, long-press inspect, and any
second tap arriving before the action resolves. The runner guarantees exactly one
matching pop on every exit path, including early exits for skip and stage-end.
HUD buttons (confirm route, limit-gauge fire) bypass _handle_click entirely,
so they remain interactive regardless of the gate. The await_action step type
with free_move: true relies on this: the player can move Katyusha freely into
position, then tap the limit gauge button to fire Iron Curtain, and the step
advances on action_resolved.
Skip design¶
Three scopes, cheapest first.
The per-segment skip glyph (the "x" inside the coachmark bubble) cancels the current segment and jumps to the next. No persistent state is written. It is there so a player who already knows a mechanic does not have to perform the gesture before being allowed to proceed.
The "Skip tutorial" pill, fixed at the top-right of the board, skips the rest of the tutorial for this launch. It sets an in-memory flag on the director that survives sector transitions but clears on the next launch or retry. The intent is to hand the player back immediately without also turning off guidance forever.
The global Settings toggle (ConfigManager.invasive_tutorials_enabled, default
on) turns the entire invasive system off permanently. When off, the director is
a no-op. The passive archive and pop-ups are unaffected. The toggle is also
accessible from the pause menu.
Resuming a stage mid-sector (from a save, or after a crash) auto-sets the per-launch skip flag so the player is not dropped back into sector-1 guidance when they are already past it.
Curriculum¶
The Chapter 1 script covers all ten stages. Each stage introduces one or two new mechanics and reinforces the previous ones. The hand-off point is after the new mechanic is demonstrated once; nothing drags the player through the full stage. Stage 1 is the most guided (four segments: select, move, inspect, attack and hand off); stages 4 through 6 and 8 through 10 are lighter (one or two short framing segments and an immediate hand-off to free play).
A curriculum rule: nothing is taught twice. One segment per new idea. A later sector that reuses an idea gets at most a one-line reminder.
The curriculum design went through several revisions during playtesting. Two examples illustrate the kind of change that came up:
Stage 1's first guided move was originally to a cell inside one tower's range, so the player got hit on the teaching move. That was changed to a safe cell first, then a deliberate approach to the nearer tower for the attack lesson. The reactive fire is now taught intentionally rather than as a surprise side effect of the movement lesson.
Stage 2 sector 1 originally had Nadeshiko move to a specific cell, then attack from there, with two separate guided steps. Playtesters moved her there and then were confused about why the attack step was targeting something she could already reach. The steps were collapsed: one guided action where Nadeshiko approaches and attacks the anti-ground sentinel, with the flight-over-mountain and anti-ground immunity conveyed by the coachmark text and the staged approach.
Coverage today¶
Chapter 1 has a full script for all ten stages. Chapter 2 has partial coverage (stages 1, 8, and 10). Chapter 3 has partial coverage (stages 2, 3, 7, 9, and 10). The director's chapter gate currently accepts 1 through 3, so Chapter 2 and 3 scripts fire automatically once authored. Chapters 4 through 6 have no invasive scripts yet; those chapters are far enough out that drafting curriculum before the stages are stable would produce stale scripts.
Testing¶
The test suite for the tutorial system (tests/test_tutorial_director.gd and
tests/test_tutorial_manager.gd) covers data integrity checks on every authored
script, the skip-scope state machine, ref resolution against stubbed stage
state, and the input-block balance guarantee. The length budget check (each
text_key body must be 64 characters or fewer and at most two sentences) runs
against the live CSV at test time so an over-long coaching line fails the suite
before it ships.