Skip to content

Add Tutorial Island - #1259

Open
HarleyGilpin wants to merge 7 commits into
GregHib:mainfrom
HarleyGilpin:feat/tutorial-island
Open

Add Tutorial Island#1259
HarleyGilpin wants to merge 7 commits into
GregHib:mainfrom
HarleyGilpin:feat/tutorial-island

Conversation

@HarleyGilpin

@HarleyGilpin HarleyGilpin commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

New accounts start on Tutorial Island and walk the full 68 stage sequence through all nine instructors, ending with the Magic Instructor teleporting them to Lumbridge with the usual starter kit.

Gated behind world.start.tutorial, default on. With it off, nothing changes.

How it works

AccountManager.create seeds a persisted tutorial_stage int, so only accounts created while the setting is on ever enter and existing saves are untouched. Everything else derives from that one value, including which tabs are locked, so relogging mid-tutorial restores the stage, tabs, hint arrow, progress bar and instructions without storing any of them separately.

Stage data is in tutorial_island.tables.toml, one row per stage: title, instruction lines, hint target, tab to flash, component to reveal. advanceTutorial(from) only moves when the player is on exactly from, so a handler can be registered without re-checking the stage.

::tutorial <stage> jumps to any stage for testing.

Interface notes

The instruction box (372) has its own interface type rather than reusing dialogue_box.

The progress bar (371) goes in a new above_chat_box slot, 548:17 / 746:74, the 512x69 bottom anchored strip that pairs with private_chat. Its varp is the segment count plus one, because client script 1437 lights segment n only when varp 406 exceeds n. Component 4 stays hidden; it holds a game screen sized arrow model that otherwise spills out of the strip.

Tab flashing uses varbit 3756. Script 117 guards the range to 1..20 and the value list matches the existing tab varc offset by one.

Testing

./gradlew test passes. Content tests cover spawn routing with the setting on and off, the advance rule, tab unlocking, gated doors and ladders in both directions, burnt bread still finishing the cooking stage, the chef replacing lost ingredients, smelting completing, the instruction box yielding to dialogue, the banker keeping its own conversation, the first run orb click, and the welcome message waiting until the player has landed.

Played through end to end in the client.

The client frames opcode 69 as a fixed 12 bytes, but arrowHint only wrote
the body when a sprite was given. clearHint passes the default sprite of
-1, so clearing an arrow sent a 2 byte packet, and the client consumed the
following 10 bytes as part of it. That desynchronised the stream and
dropped the connection on the next opcode.

Every branch now writes the full body, padding the clear case the client
skips over. Covered for npc, player, tile and both clear forms.
New accounts start on Tutorial Island and walk the 68 stage sequence
through all nine instructors, ending with the Magic Instructor teleporting
them to Lumbridge with the usual starter kit. Gated behind
world.start.tutorial, default on; with it off nothing changes.

The stage counter is a persisted int seeded by AccountManager.create, so
only accounts made while the setting is on ever enter, and existing saves
are untouched. Stage data lives in tutorial_island.tables.toml: text,
hint target, tab to flash and component to reveal, one row per stage.
Locked tabs are derived from the stage rather than stored separately, so
relogging restores everything from the one value.

The instruction box (interface 372) gets its own interface type rather
than reusing dialogue_box. Queued actions only run while Player.dialogue
is null, so an always-on interface in a dialogue slot freezes every
delayed action, smelting included. It also stands aside while an NPC is
talking, since both share the chat box slot on the client. The progress
bar (371) sits in a new above_chat_box slot, and its varp is the segment
count plus one because the client script lights segment n only when the
varp exceeds n.

Fixes found along the way:

- Introduction read world.setup.gear, but the property is
  world.start.gear, so the toggle never did anything. It also granted
  "shrimp", which is not an item id.
- fishing_spot_tutorial_island had an empty net list and caught nothing.
- Two Tutorial Island ladders (3029, 3030) were filed under wizards tower
  with wrong region comments. Object teleports are keyed by tile and
  option alone, so they collided with the correctly named entries.
- The dungeon doorways are named Gate in the cache but swing as doors,
  so they take the existing gate = false override.
- The run orb showed a stale mode when revealed mid-session, since varp
  173 is never sent while it holds its default.
The exit followed world.home, which is the respawn point rather than
where the tutorial drops you. Split it into its own
world.start.tutorial.exit setting so the two can differ.
Upstream fixed the hint arrow packet length independently in GregHib#1236, so
that implementation is taken over mine. The length test is kept, since
upstream has none and it pins every branch to twelve bytes.

Ports the tutorial to the renamed hint API: hint is now markHint, and
clearHint() with no arguments is now clearHints().
@V0lcanic

V0lcanic commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Out of curiosity, are there any plans for Unstable Foundations to be added as well? That tutorial was used up until 647, so it would be more authentic to use.

@Ebp90

Ebp90 commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Out of curiosity, are there any plans for Unstable Foundations to be added as well? That tutorial was used up until 647, so it would be more authentic to use.

Greg said to could be a toggle. Personally I have no desire to add it, Tutorial Island does it's job way better.

* registration in [content.entity.player.modal.GameFrame] so both run.
*/
class TutorialTabs : Script {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like orbs aren't opened correctly when doing the tutorial

Image

They should all be there from the start:
https://youtu.be/g6f4qib2ZSI?t=31

@HarleyGilpin HarleyGilpin Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orbs are all open from the first stage now, only the sidebar tabs are still revealed one at a time. energy_orb was the odd one out, gated to stage 22 alongside the run-orb lesson; it's in the always-open set and that stage keeps just the flash.

11e6a8f

* instructors hand out, and the player can't teleport away or be traded with.
*/
class TutorialRestrictions : Script {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Achievements should be disabled during the tutorial

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. TaskSystem rolls the variable back when a task is set during the tutorial, so it stays available to earn properly afterwards rather than being silently marked complete.

11e6a8f

id = 3045
examine = "The bank teller will serve you from here."

[cooking_range_tutorial_island]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Use" range doesn't work (dialogue doesn't open) only item on range

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cooking had no object handler at all, only item-on-object, so "Use" did nothing on any range or fire. Added objectOperate("Use", "fire_*,cooking_range*") which lists the cookable items in your inventory and runs the same cook loop.

11e6a8f


# Send new accounts through Tutorial Island
world.start.tutorial=true

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great to have a toggle to add a dialogue at the start to skip the tutorial

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added world.start.tutorial.skippable, off by default. With it on, the guide's opening dialogue offers "No, send me to the mainland", which runs the same completion path as finishing properly: standard kit, tabs restored, teleport out.

11e6a8f

id = 3039
examine = "A hot flame cooks the food."

[furnace_tutorial_island]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Use" furnace also not working

@HarleyGilpin HarleyGilpin Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same class of thing in a different script, the tutorial furnace's option is "Use" but Furnace only registered "Smelt". Registered "Use" against furnace* as well.

11e6a8f

npc<Neutral>("Lost your tools? Here's a replacement.")
return@npcOperate
}
npc<Neutral>("Keep at it. Mining and Smithing are the backbone of any adventurer's kit.")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dialogue says speak to the guide at any time to recap, guess we need to find the correct dialogue for that?

@HarleyGilpin HarleyGilpin Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, three stage texts promise a recap (Survival Expert, Lev and the Mining Instructor) and none of them had one. Each now recaps what they taught in its fallback branch instead of a generic line.

11e6a8f

"filter_buttons",
"private_chat",
"health_orb",
"prayer_orb",

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error when trying to turn on quick prayers before getting to that section (might be pre-existing?)

java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.util.List (java.lang.Integer and java.util.List are in module java.base of loader 'bootstrap')
	at content.skill.prayer.list.QuickPrayers$4.invokeSuspend(QuickPrayers.kt:59)
	at content.skill.prayer.list.QuickPrayers$4.invoke(QuickPrayers.kt)
	at content.skill.prayer.list.QuickPrayers$4.invoke(QuickPrayers.kt)
	at world.gregs.voidps.engine.client.ui.InterfaceApi$Companion.option(InterfaceApi.kt:143)
	at world.gregs.voidps.engine.client.instruction.handle.InterfaceOptionHandler$validate$2.invokeSuspend(InterfaceOptionHandler.kt:42)
	at world.gregs.voidps.engine.client.instruction.handle.InterfaceOptionHandler$validate$2.invoke(InterfaceOptionHandler.kt)
	at world.gregs.voidps.engine.client.instruction.handle.InterfaceOptionHandler$validate$2.invoke(InterfaceOptionHandler.kt)
	at world.gregs.voidps.engine.client.instruction.InterfaceHandlerKt$protectedAccess$1.invokeSuspend(InterfaceHandler.kt:138)

@HarleyGilpin HarleyGilpin Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-existing, and a fresh account hits it every time. QuickPrayers.kt:39 did set(TEMP_QUICK_PRAYERS, get(getQuickVarKey(), 0)), that 0 default stores an Int in a variable line 59 reads as a List, so it only throws for players who have never picked quick prayers. Default is now emptyList().

11e6a8f


private suspend fun Player.finish() {
npc<Happy>("Well done, you've completed the tutorial!")
npc<Neutral>("You're ready to enter the world proper. Would you like me to send you to Lumbridge now?")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These dialogues don't seem to be correct? (guess it depends which era/source you used)

https://youtu.be/8b-TGUTFaOc?t=212

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Player: Hello.

  2. Magic instructor: Good day, newcomer. My name is Terrova. I'm here to tell you about Magic. Let's start by opening your spellbook.

  3. title = "Open up your final tab."
    Lines = "Open up the Magic Spellbook tab by clicking on the flashing icon next to the Prayer List tab you just learned about."

  4. Magic instructor: Good. This is a list of your spells. Currently you can only cast one offensive spell called Wind Strike. Let's try it out on one of those chickens.

  5. Terrova gives you five air runes and five mind runes!

  6. title = "Cast Wind Strike at a chicken."
    lines = ["Now you have runes you should see the Wind Strike icon at the", "top left corner of the Magic interface - third in from the", "left. Walk over to the caged chickens, click the Wind Strike icon", "and then select one of the chickens to cast it on. It may take", "several tries. If you need more runes ask Terrova."]

  7. title = "You have almost completed the tutorial!"
    lines = ["All you need to do now is teleport to the mainland. Just speak with", "Terrova and he'll tell you how to do that."]

  8. Magic instructor: "Well you're all finished here now. I'll give you a reasonable number of runes when you leave."

  9. Option Title: Do you want to go to the mainland?
    Option 1: Yes.
    Option 2: No.

If Yes:

  1. Player: Yes, I'm ready to leave.
  2. Magic instructor: Good good. I've deactivated the protective spells around the island, so now you can teleport yourself out of here.
  3. Magic instructor: "When you get to the mainland you will find yourself in the town of Lumbridge. If you want some ideas on where to go next, talk to my friend Phileas, also known as the Lumbridge Guide. You can't miss him; hes"
  4. Magic instructor: "holding a big staff with a question mark on the end. He also has a white beard and carries rucksack full of scrolls. There are also tutors willing to teach you about the many skills you could learn."
  5. item("question_mark", "When you get to Lumbridge, look for this icon on your minimap. The Lumbridge Guide and the other tutors will be standing near one of these. The Lumbridge Guide should be standing slightly to the north-east of")
  6. item("question_mark", "the castle's courtyard and the others you will find scattered around Lumbridge.")
  7. Magic instructor: "If all else fails, visit the RuneScape website for a whole chestload of information on quests, skills and minigames as well as a very good starter's guide."
  8. title = "You have almost completed the tutorial!"
    Lines = "Just click on the first spell, Home Teleport, in your Magic Spellbook. This spell doesn't require any runes, but can only be cast once every 30 minutes."

@HarleyGilpin HarleyGilpin Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewritten from the youtube transcript.

11e6a8f

@HarleyGilpin HarleyGilpin Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to the Home Teleport ending as well, so this now follows the transcript through to the end.

Terrova's farewell points at the spellbook, a closing stage carries the "Just click on the first spell, Home Teleport" text, and casting it is what completes the tutorial. It's the one spell allowed through the teleport block, and only on that stage, everything else stays blocked. The spell does the travelling, so world.start.tutorial.exit now only applies to the optional skip, where no spell is involved.

980da95

- All orbs are present from the first stage; only sidebar tabs are still
  revealed one at a time.
- Tasks no longer complete on the island. The variable is rolled back so
  the task stays available afterwards.
- Furnaces and ranges offering "Use" rather than "Smelt" now work.
  Tutorial Island's furnace and range both use "Use", and cooking had no
  object handler at all, only item-on-object.
- world.start.tutorial.skippable lets the guide offer to skip straight to
  the mainland. Off by default.
- The Survival Expert, Master Chef and Mining Instructor now recap what
  they taught, which their stage text already promised.
- Magic Instructor dialogue replaced with the transcript, including the
  farewell about Phileas and the tutors.

Also fixes a pre-existing crash: QuickPrayers stored an Int default in a
variable everything else reads as a list, so selecting quick prayers threw
a ClassCastException for anyone who had never picked any.
Terrova's farewell now points at the spellbook instead of teleporting the
player, matching the original: a closing stage asks them to cast Home
Teleport, and casting it is what completes the tutorial. The spell does
the travelling, so the player lands where Home Teleport puts them.

Home Teleport is the one spell allowed through the teleport block, and
only on that stage. The guide's optional skip still teleports directly to
world.start.tutorial.exit, since no spell is involved there.
The two lines about finding the Lumbridge Guide are item boxes showing
the question mark icon, as in the original. Item 5079 is nameless in the
cache but already declared as questionmark_icon.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants