Skip to main content

Tutorial Island

TutorialIslandPlugin automates Tutorial Island start to finish. The manifest registers it under config group n3tutorialisland; the plugin ships disabled by default.

Architecture

  • TutorialIslandPlugin - lifecycle, overlay and side-panel registration, debug-stepping hotkeys, delegates each tick to the controller, exposes the latest snapshot.
  • TutorialIslandController - pure per-tick decision tree; at most one action per tick. Holds varp tracking, fixed-sequence TaskPipelines, halt state, the per-account seed/profile, and manual step state.
  • TutorialIslandRuntime - interface that isolates every RuneLite/SDK read and action from the controller, so controller logic stays unit-testable.
  • RuneLiteTutorialIslandRuntime - live adapter implementing the runtime against InteractionApi, sdk.query, sdk.client, and the walker.
  • TutorialStage - enum plus the pure fromVarp(int) mapping from progress varp to a named stage.
  • TutorialConstants - widget/object IDs, NPC/object names, the step-to-tab map, and area bounding boxes.
  • TutorialAccountProfile - pure, seeded per-account profile: gather targets, appearance click counts, and action-gap ticks.
  • TutorialNameGenerator - seeded name generator with username-file and CSV word-list inputs (ported from the AIO AccountNameGenerator).
  • TutorialIslandSnapshot - immutable overlay-safe view of stage, varp, status text, halt reason, manual mode, gather targets, and the chosen username.
  • TutorialIslandOverlay - branded N3Overlay rendering the snapshot.
  • TutorialIslandPanel - branded N3PluginPanel with Step Action, Step Stage, and Resume controls.
  • UsernameGenerator - the original random name generator; retained with tests but no longer used by the controller (superseded by TutorialNameGenerator).

Core behavior

onGameTick calls TutorialIslandController.onGameTick() and stores the returned snapshot. The global ActionPacer and Walker advance from PacketUtilsPlugin.onGameTick; see Suite Wiring. The plugin registers with Break Handler and is active there only while auto mode is running, the tutorial is incomplete, and the controller is not halted. Manual step mode stops break tracking; Resume re-enters active break tracking. A due or active break releases the input lock and skips the controller tick before any tutorial action.

Progress is driven entirely by varp 281 (TutorialConstants.VARP_TUTORIAL_PROGRESS):

  1. Read the current varp each tick.
  2. On a varp change, reset fixed-sequence pipelines and log the transition when debugLogging is on.
  3. Map the varp to a TutorialStage via the pure TutorialStage.fromVarp.
  4. Dispatch the stage to its handler, which performs at most one interaction.

The stage advances only as a function of varp change, never on a timer. A varp of 1000 or higher maps to COMPLETE. Several raw values collapse to the same stage (for example 20 and 60 both map to SURVIVAL_TALK); the switch in fromVarp is the authority for that mapping.

Stage flow

fromVarp covers the full tutorial in order: name entry and appearance, Gielinor Guide, Survival Expert (woodcutting, fishing, firemaking, cooking), Master Chef (dough, bread), Quest Guide, mining and smithing, the Combat Instructor (melee then ranged on rats), bank and poll booths, the Account Guide, Brother Brace (prayer), and the Magic Instructor (cast Wind Strike on a chicken), ending in COMPLETE.

Two stages need a runtime decision the varp alone cannot give:

  • varp 1 returns NAME_ENTRY; the controller switches to APPEARANCE when the appearance arrows are already on screen.
  • varp 650 returns CAST_WIND_STRIKE; the controller casts whenever a chicken is in range and only walks toward the cast area when none is found (cast-first, no hard area gate).

Sub-steps split across ticks

Some stages spread work over several ticks to keep one action per tick:

  • Name entry: click field, type username, look up name, set name. The three button clicks fire via raw op-1 (see "Interface clicks" below).
  • Appearance: click each arrow group a seeded number of times (TutorialAccountProfile.appearanceClicksForArrow), then confirm.
  • Ranged setup: continue dialogue, open inventory, wield shortbow, wield arrows, attack.
  • Equipment stats: open the equipment tab, click the stats panel (raw op-1), then wield the dagger.

The fixed-order sequences above are driven by TaskPipeline; transient InteractionResult statuses such as pacing or temporarily missing widgets retry the current step and do not advance it. Observation loops such as dialogue, gathering, walking, and casting remain plain per-tick handlers because the live tutorial varp and game state own their progress.

Before dispatch, TutorialIslandController drains any visible Continue prompt or dialogue option. This includes the rev-239 packed Continue widget 12648448 (193:0), whose live adapter uses a direct widget interaction after the normal lookup and recursive widget-root fallbacks. Every issued action is recorded with its returned InteractionStatus; paced, temporarily missing target/widget, and movement-in-progress results retry, while terminal results halt with the stage, varp, action, status, and target in the snapshot/log.

Interface clicks (raw op-1)

Several Tutorial Island interface components carry no named menu action - the name field, the "look up"/"set name" buttons, the smithing dagger entry, and the worn-equipment stats button. The action-name path (WidgetActions.interact(...)) resolves the action string to a 1-based op index and fails with ACTION_NOT_FOUND when there is none, so those clicks silently never queued. The controller now calls runtime.clickWidgetOp1(group, child) for them, which maps to WidgetActions.clickOp(packedId, 1) and fires op-1 directly, bypassing action-name resolution. The op index is assumed to be 1 for each; confirm on a live run.

Doors and walk-before-talk

Room transitions go through walkOpeningDoors(area): when the player is outside the target area and the collision walker has pulled up stationary adjacent to an object exposing an "Open" action, it opens that object once; otherwise it walks toward the area. Door state is reset only when the tutorial varp changes, so a visible door cannot be toggled repeatedly. The helper covers the Survival Expert, Master Chef, Quest Guide, Brother Brace, and Magic Instructor approaches. The saved Quest Guide anchors remain (3072,3090,0)(3086,3126,0); the house door is 9716 and the outer tutorial door is 9710.

Talk stages that can be reached from out of range (SURVIVAL_TALK, QUEST_GUIDE_TALK, COMBAT_TALK, COMBAT_TALK_2, BROTHER_BRACE_TALK) go through walkToOrTalk(npc): talk when the NPC is reachable, otherwise step toward its location first.

Making bread dough (MAKE_DOUGH) is guarded: it skips when Bread dough is already in the inventory or the player is animating, which prevents repeated flour/water combines on every tick. The item names (Pot of flour, Bucket of water) are best-effort for the current rev.

Halt conditions

The controller halts when it cannot make progress:

  • The name/appearance widget stays absent for more than 20 ticks.
  • An UNHANDLED varp persists for more than 60 ticks.
  • The walker reports PATH_NOT_FOUND for the mining area.

Reaching COMPLETE also stops the controller, with no halt reason set. The halt reason surfaces in the overlay snapshot.

Per-account seed and profile

On the first logged-in tick the controller derives a per-account seed and builds a TutorialAccountProfile. The seed comes from seedOverride when non-zero, otherwise from a SplitMix64 mix of the account key (client.getUsername(), falling back to the local player name). The profile draws gather targets, appearance click counts, and action-gap ticks from that seed, so the same account always produces the same character and gathering pattern while different accounts diverge.

actionGapTicks() returns 0 to 2 extra idle ticks inserted before each dispatched action in auto mode, so dispatch cadence varies per account.

Multi-gather

Each gathering skill can over-collect before the tutorial advances. The controller gates woodcutting, fishing, and mining on inventoryCount against the profile's seeded target, not a simple "has one" check:

  • Woodcutting (handleWoodcutting, varp 70): visible dialogue is drained before the chop gate, then the player keeps chopping until Logs reaches chopTarget. The pure-talk varps 20/60 use the separate talk-only handleSurvivalTalk and never chop (chopping at varp 60 soft-locked the talk that advances 60 -> 70).
  • Fishing: nets until Raw shrimps reaches fishTarget.
  • Mining: mines until Tin ore / Copper ore reaches mineTarget.

With a skill's toggle off the target is fixed at 1, matching the original collect-one-then-advance behaviour.

Caution

Multi-gather assumes the tutorial varp does not force-advance after the first resource pickup. Confirm on a live run; if the varp advances mid-gather, the count gate is moot and the over-collection will not happen.

Debug stepping

The controller runs in NORMAL or MANUAL mode. In manual mode it pauses before each dispatch and the status reads Paused (manual). Three hotkeys and three panel buttons drive it:

  • Step Action (stepActionHotkey): dispatch exactly one action, then pause.
  • Step Stage (stepStageHotkey): keep dispatching until the tutorial varp changes, then pause.
  • Resume (resumeHotkey): leave manual mode and dispatch automatically.

Set startPaused to begin in manual mode. The per-account action gap applies only in auto mode.

Username generation

TutorialNameGenerator.generate(seed, pool, prefixes, suffixes) picks one of four seeded strategies: a direct pool pick, prefix+suffix+number, prefix+number, or suffix+number. Empty word lists fall back to bundled defaults. parseWordList splits the CSV config values; loadUsernamePool reads one name per line from the username file, skipping blank lines and # comments, and returns an empty list on any read error. The controller seeds generation with the account seed, so a given login reproduces the same name.

UsernameGenerator.generate(Random) (the original generator) stays in the tree with its tests but the controller no longer calls it.

Branding

The overlay extends N3Overlay and the side panel extends N3PluginPanel, so both carry the amber n3 brand pill and status dot. The plugin registers a NavigationButton with a deliberately blank icon from TutorialIslandPlugin.navIcon()

  • a transparent 16×16 BufferedImage with no reference to N3Branding or n3.png. The nav button does not load the n3 logo: the earlier N3Branding.scaledLogo(16) call ran inside startUp() and throws IllegalStateException on any brand-asset load/decode failure, which RuneLite treats as a failed plugin start. Loading a blank icon keeps start-up safe (NavigationButton.builder() still requires a non-null icon at build()). The plugin-list [n3] marker (N3PluginListBranding) and the panel header logo (HeaderPill) are unaffected - both already degrade safely on asset failure. The panel's Step Action, Step Stage, and Resume buttons call the matching controller delegates, and refreshUi pushes each tick's snapshot to the panel.

Configuration

Config group n3tutorialisland:

  • showOverlay - show the status overlay (default true).
  • debugLogging - log stage transitions and unhandled varp values (default false).

Multi-gather section:

  • chopMultiple, fishMultiple, mineMultiple - enable over-collection per skill (default false).
  • multiGatherMin, multiGatherMax - inclusive bounds for the seeded gather target (default 2 and 5).

Debug stepping section:

  • startPaused - begin in manual mode (default false).
  • stepActionHotkey, stepStageHotkey, resumeHotkey - manual stepping hotkeys (default unset).

Identity section:

  • usernameFile - absolute path, or a file under ~/.runelite/n3Plugins/tutorialisland/.
  • namePrefixes, nameSuffixes - comma-separated word lists for generated names.
  • seedOverride - fixed per-account seed; 0 derives the seed from the account name.

ID verification

The IDs in TutorialConstants were checked against the rev238 gameval sources. These are confirmed:

ConstantValuegameval authority
VARP_TUTORIAL_PROGRESS281VarPlayerID.TUTORIAL
NAME_WIDGET_GROUP558InterfaceID.TUTORIAL_DISPLAYNAME
NAME_INPUT_CHILD6Live-verified display-name entry component on rev239
NAME_TEXT_CHILD12TutorialDisplayname.NAME_TEXT (0x022e_000c)
NAME_LOOKUP_CHILD18TutorialDisplayname.LOOK_UP_NAME (0x022e_0012)
NAME_SETNAME_CHILD19TutorialDisplayname.SET_NAME (0x022e_0013)
APPEAR_PARENT_GROUP679InterfaceID.PLAYER_DESIGN
APPEAR_ACCEPT_CHILD74PlayerDesign.CONFIRM (0x02a7_004a)
FAMILIARITY_WIDGET_GROUP929Live-verified Past Experience interface on rev239
FAMILIARITY_EXPERIENCED_CHILD7Live-verified Experienced player option with Select action
SMITHING_WIDGET_GROUP312InterfaceID.SMITHING
SMITHING_DAGGER_CHILD9Smithing.DAGGER (0x0138_0009)
EQUIP_STATS_WIDGET_GROUP387InterfaceID.WORNITEMS
EQUIP_STATS_CHILD1Wornitems.EQUIPMENT (0x0183_0001)
DIALOGUE_WIDGET_GROUP219InterfaceID.CHATMENU
RAT_GATE_OBJECT_ID9719ObjectID.NEWBIEDOOR5_L

The original plugin shipped the four name/appearance child indices wrong (NAME_TEXT_CHILD, NAME_LOOKUP_CHILD, NAME_SETNAME_CHILD, APPEAR_ACCEPT_CHILD); they were corrected to the gameval values above.

Tutorial Island has no account-bootstrap integration. Bootstrap requires side tabs that the tutorial has not unlocked yet and would prevent controller ticks, including manual Step Action requests.

Still unconfirmed, pending a live run with the widget inspector:

  • POLL_WIDGET_GROUP = 229 is InterfaceID.MESSAGEBOX, a generic message box; POLL_WIDGET_CHILD = 1 is unverified.
  • Area bounding boxes in TutorialConstants.
  • Op index 1 for the actionless interface clicks (name field/lookup/set-name, smithing dagger, equipment stats).
  • Object and NPC names (Tree, Fire, Range, Furnace, Anvil, Gate, and the rest). The post-2023-07-12 tree rename did not touch the tutorial, but confirm the names resolve.

The TAB_FOR_STEP step-to-tab map was realigned 2026-05-31 to the canonical varp 281 sequence: 3→Settings, 30→Inventory, 50→Skills, 230→Quests, 390→Equipment, 430→Combat, 560→Prayer, 580→Friends, 630→Magic. The talk-only steps 530/532 were removed from the map. 531 ("open the account-management tab") stays mapped to Tab.LOGOUT as a best-effort placeholder because the Tab enum has no account-management value - confirm this one on a live run.

Testing

JUnit 4 tests cover the package, including controller result/retry and door-transition tests:

  • TutorialStageTest - the full fromVarp mapping (including varps 200, 671, 680), the COMPLETE threshold, and UNHANDLED defaults.
  • TutorialConstantsTest - pins TAB_FOR_STEP to the canonical varp 281 tabs and asserts the talk-only steps 530/532 are absent.
  • UsernameGeneratorTest - length, digit placement, reproducibility, variation.
  • TutorialAccountProfileTest - seeded gather targets, min/max bounds, toggle-off pinning, action-gap and appearance-click bounds, per-account variation.
  • TutorialNameGeneratorTest - the four strategies, reproducibility, parseWordList, and loadUsernamePool.
  • TutorialIslandControllerTest - multi-gather gating, manual step mode, seeded name generation, raw op-1 interface clicks (smithing/equipment/name field), walk-before-talk when an NPC is unreachable, the talk-only Survival Expert handler not chopping at varp 60, the make-dough guard, and cast-first Wind Strike, driven through FakeTutorialIslandRuntime.
  • TutorialIslandPluginTest - the nav-button icon seam: navIcon() is non-null, 16×16 TYPE_INT_ARGB, and never throws (independent of n3.png).

The raw op-1 helper itself is covered by WidgetActionsTest (null-widget and hidden-widget guards on clickOp).

FakeTutorialIslandRuntime implements the runtime interface with mutable read fields and recorded actions, so the controller is tested with no MockedStatic. GameStateGuard reads the client via N3Client.getClient(), which returns null in the test JVM.

Live validation (pending)

Name entry and appearance, every guide and skill station in fromVarp order, the melee and ranged rat fights, bank and poll booth use via hint arrows, the prayer and magic stations, and a clean COMPLETE on a fresh account. Confirm every hypothesised ID in TutorialConstants before relying on a run. Also confirm:

  • The tutorial varp does not force-advance mid-gather, so multi-gather over-collects to the target before progressing.
  • The appearance arrow widgets resolve so seeded click counts land on real arrow groups.
  • Two accounts with different logins receive different names and appearances.
  • Manual stepping pauses and steps action-by-action and stage-by-stage as expected.
  • The raw op-1 clicks land on the right action for the name field/lookup/set-name, the smithing dagger, and the equipment-stats button.
  • walkOpeningDoors opens room-transition doors so the walker does not stall against them, and walkToOrTalk closes the gap to out-of-range NPCs.
  • The magic finale completes: varps 620680 continue the dialogue, cast-first Wind Strike lands on a chicken, and the 671/680 teleport advances to COMPLETE without an UNHANDLED halt.
  • MAKE_DOUGH combines flour and water exactly once (item names resolve on the current rev).

Tutorial Island Decision Tree

The following diagram visualizes the varp-281 driven stage routing and sub-step execution flow of the Tutorial Island controller: