Skip to main content

Step

A step is an order number plus the actions that run while it is active.

Steps are plain data. Nothing happens when you build one; the runtime is what enters it, and that is why a tutorial can be declared far from where it runs.

Step.new(1, {
	Dialog.new({ Lines = { "Welcome!" } }),
	FocusUI.to(shopButton, { Advance = true }),
})

Every action in a step starts at the same moment and none of them blocks the others. In the example above the dialog appears while the shop button is already highlighted. The step ends when one of the actions asks it to, which here is the click on the button.

Types

Action

interface Action {
Kindstring--

Which action this is, for debugging

Run(contextActionContext) → ()--

Called once, when the step opens

}

What the action constructors return, and all a step ever holds.

Actions of a step all start together. None of them blocks the others; the step ends when an action asks the context to advance.

You rarely build one by hand. Custom.new is the supported way to write your own, because it gets you the same context and the same cleanup as the built-in actions.

Functions

new

constructor
Step.new(
ordernumber,--

Position in the flow. A positive integer

actions{Action}?--

Actions to start together. Defaults to none

) → Step

Declares one step of a tutorial.

Order numbers decide the sequence, not the position in the list you pass to Tutorial.build. They do not need to be contiguous, and leaving gaps is the cheap way to keep room for steps you expect to add later:

Tutorial.build({
	Step.new(30, { PointWorld.to(fountainPart) }),
	Step.new(10, { Dialog.new({ Lines = { "Welcome!" } }) }),
	Step.new(20, { FocusUI.to(shopButton, { Advance = true }) }),
})

That runs 10, then 20, then 30, and inserting a step between the first two later costs nothing.

A step with no actions is valid. It shows nothing and waits, which is occasionally what you want while the server decides where to send the player next.

Errors

TypeDescription
"TutorialKit step order must be a positive integer"order was zero, negative or fractional
Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "Declares one step of a tutorial.\n\nOrder numbers decide the sequence, not the position in the list you pass to [Tutorial.build].\nThey do not need to be contiguous, and leaving gaps is the cheap way to keep room for steps you\nexpect to add later:\n\n```lua\nTutorial.build({\n\tStep.new(30, { PointWorld.to(fountainPart) }),\n\tStep.new(10, { Dialog.new({ Lines = { \"Welcome!\" } }) }),\n\tStep.new(20, { FocusUI.to(shopButton, { Advance = true }) }),\n})\n```\n\nThat runs 10, then 20, then 30, and inserting a step between the first two later costs nothing.\n\nA step with no actions is valid. It shows nothing and waits, which is occasionally what you want\nwhile the server decides where to send the player next.",
            "params": [
                {
                    "name": "order",
                    "desc": "Position in the flow. A positive integer",
                    "lua_type": "number"
                },
                {
                    "name": "actions",
                    "desc": "Actions to start together. Defaults to none",
                    "lua_type": "{Action}?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Step"
                }
            ],
            "function_type": "static",
            "tags": [
                "constructor"
            ],
            "errors": [
                {
                    "lua_type": "\"TutorialKit step order must be a positive integer\"",
                    "desc": "order was zero, negative or fractional"
                }
            ],
            "source": {
                "line": 55,
                "path": "src/Core/Step.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "Action",
            "desc": "What the action constructors return, and all a step ever holds.\n\nActions of a step all start together. None of them blocks the others; the step ends when an\naction asks the context to advance.\n\nYou rarely build one by hand. [Custom.new] is the supported way to write your own, because it\ngets you the same context and the same cleanup as the built-in actions.",
            "fields": [
                {
                    "name": "Kind",
                    "lua_type": "string",
                    "desc": "Which action this is, for debugging"
                },
                {
                    "name": "Run",
                    "lua_type": "(context: ActionContext) -> ()",
                    "desc": "Called once, when the step opens"
                }
            ],
            "source": {
                "line": 153,
                "path": "src/Types.luau"
            }
        }
    ],
    "name": "Step",
    "desc": "A step is an order number plus the actions that run while it is active.\n\nSteps are plain data. Nothing happens when you build one; the runtime is what enters it, and\nthat is why a tutorial can be declared far from where it runs.\n\n```lua\nStep.new(1, {\n\tDialog.new({ Lines = { \"Welcome!\" } }),\n\tFocusUI.to(shopButton, { Advance = true }),\n})\n```\n\nEvery action in a step starts at the same moment and none of them blocks the others. In the\nexample above the dialog appears while the shop button is already highlighted. The step ends\nwhen one of the actions asks it to, which here is the click on the button.",
    "source": {
        "line": 21,
        "path": "src/Core/Step.luau"
    }
}