Add Tutorial Island - #1259
Conversation
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().
|
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 { | ||
|
|
There was a problem hiding this comment.
Looks like orbs aren't opened correctly when doing the tutorial
They should all be there from the start:
https://youtu.be/g6f4qib2ZSI?t=31
There was a problem hiding this comment.
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.
| * instructors hand out, and the player can't teleport away or be traded with. | ||
| */ | ||
| class TutorialRestrictions : Script { | ||
|
|
There was a problem hiding this comment.
Achievements should be disabled during the tutorial
There was a problem hiding this comment.
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.
| id = 3045 | ||
| examine = "The bank teller will serve you from here." | ||
|
|
||
| [cooking_range_tutorial_island] |
There was a problem hiding this comment.
"Use" range doesn't work (dialogue doesn't open) only item on range
There was a problem hiding this comment.
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.
|
|
||
| # Send new accounts through Tutorial Island | ||
| world.start.tutorial=true | ||
|
|
There was a problem hiding this comment.
It'd be great to have a toggle to add a dialogue at the start to skip the tutorial
There was a problem hiding this comment.
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.
| id = 3039 | ||
| examine = "A hot flame cooks the food." | ||
|
|
||
| [furnace_tutorial_island] |
There was a problem hiding this comment.
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.
| 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.") |
There was a problem hiding this comment.
The dialogue says speak to the guide at any time to recap, guess we need to find the correct dialogue for that?
There was a problem hiding this comment.
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.
| "filter_buttons", | ||
| "private_chat", | ||
| "health_orb", | ||
| "prayer_orb", |
There was a problem hiding this comment.
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)There was a problem hiding this comment.
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().
|
|
||
| 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?") |
There was a problem hiding this comment.
These dialogues don't seem to be correct? (guess it depends which era/source you used)
There was a problem hiding this comment.
-
Player: Hello.
-
Magic instructor: Good day, newcomer. My name is Terrova. I'm here to tell you about Magic. Let's start by opening your spellbook.
-
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." -
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.
-
Terrova gives you five air runes and five mind runes!
-
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."] -
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."] -
Magic instructor: "Well you're all finished here now. I'll give you a reasonable number of runes when you leave."
-
Option Title: Do you want to go to the mainland?
Option 1: Yes.
Option 2: No.
If Yes:
- Player: Yes, I'm ready to leave.
- Magic instructor: Good good. I've deactivated the protective spells around the island, so now you can teleport yourself out of here.
- 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"
- 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."
- 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")
- item("question_mark", "the castle's courtyard and the others you will find scattered around Lumbridge.")
- 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."
- 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."
There was a problem hiding this comment.
Rewritten from the youtube transcript.
There was a problem hiding this comment.
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.
- 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.
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.createseeds a persistedtutorial_stageint, 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 exactlyfrom, 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_boxslot,548:17/746:74, the 512x69 bottom anchored strip that pairs withprivate_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
tabvarc offset by one.Testing
./gradlew testpasses. 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.