Market Squad Recruitment Drive
Original Post
Making your own tutorials with Toribash 5.4+
Starting with Toribash 5.4, I added new way to create and run tutorials which doesn't require extensive lua knowledge.
This guide expands on how to create your own tutorials, what they can be used for and how to run them afterwards.
Please make sure you're using the latest available version of Toribash before you start - tutorials backend keeps receiving updates with every new version, and while 99% of what you'll see below will likely work correctly in 5.4, it's better if you have the newest version.

New tutorial system basics

New system allows (relatively) simple creation of new tutorials / events which may feature replay cutscenes, load various mods, display messages and so on. You can see it in action by loading any tutorial or ingame event (e.g. Hole in the Wall) in modern UI.

Any tutorial consists of minimum 2 data files, one of which contains tutorial instructions and the other one is a localization file for messages. Instructions are then loaded by tutorials manager class and run as an actual tutorial. This means that for simpler tutorials you won't need any lua coding knowledge.

Tutorials consist of steps and use a specific set of instructions which are similar to Toribash chat commands or Toribash-specific lua functions. All instructions can be roughly divided into two groups: those that act as conditions to pass to next step and those which don't (this is also expanded upon below). Once all requirements to end the step are met, current step is finished and the next one starts.

Creating your first tutorial

Let's say we want to make a tutorial that plays 150 frames of a replay, then shows a message and then asks the player to move one of Tori's bodyparts.
To do that, we first create two data files which will be used for the tutorial. Say the tutorial is going to be called mytutorial1 , this means we make two files named the following way:
  • mytutorial1.dat - this file will contain all tutorial instructions.
  • mytutorial1_english.txt - this file will contain all tutorial messages in English
One thing to keep in mind - your tutorial must have an English localization. You can add other localization files (and may define all the messages there), but [i]tutorialname[i]_english.txt file has to exist.

After these files are created, open .dat file with any text editor.

With the first step, we want to load a replay. We'll also hide hud and make sure current player's character is displayed on Tori spot:
OPT hud 0;
LOADREPLAY mytutorial_replay.rpl;
Every step has to begin with "STEP;" instruction. After that, we use OPT instruction to set hud to 0, load our replay and load PLAYER (current user) on spot 0 (Tori). Note that LOADREPLAY doesn't instantly play the replay - it's getting paused instantly after loading.

For second step, we play 150 frames of the replay:
This one is pretty obvious, PLAYFRAMES 150; instruction plays 150 frames and then pauses the replay.

Third step we display our message. There are two ways of showing messages to user: we can show a general "hint" message or make it look like that's some tutorial character speaking. For this one, we'll go with a character line:
SAY mtCharacter;
SHOWSAYMESSAGE; command triggers message box. You only need to run this before showing your first message to user - if we wanted to display another message instantly after this one, having this command among step instructions won't do anything.
SAY [characterName]; commands sets message author to display their name and head texture (if found in customs folder). Here's an example of how it looks.
After that, we set the message that's going to be shown to the user: MESSAGE [messageStringName]; . Message string name should correspond to one of text strings from tutorial localization file - see more below.
Last command we add is WAITBUTTON; which pauses tutorial execution until user presses enter or continue button. This is optional, but giving user some time to make sure they've read the message is usually the way to go.

After the message is shown, we need to hide message box and enter edit mode to later wait for user to move a bodypart on their character's body:

With the first step, we hide say message box, simple.
After that, we enter edit game mode (as we previously were in replay mode), show tooltip (optional) and set joint movement requirement with MOVEJOINT [jointName] [jointState]; command. This one specifically, would require the user to change their right knee's joint state to "Extending".

That's it, now we're finished with the .dat file. The only thing that remains now is to set the intro message in localization file. To do that, we put the following line in mytutorial_english.txt file:
INTROMSG	Hi, this is my first tutorial!
Pay attention to the fact that string name and the message have to be separated by tab and not just any whitespace character.

And now the tutorial is ready to be run! You can read on how to run it below.

Data file syntax

I've explained some of instructions data file commands above, this is a complete list of all currently available (as of Toribash 5.42) commands:

Notice: all commands must be followed by semicolon (;)
Command arguments preceded with ? are optional.
Commands marked with * act as conditions to pass to next step.
Commands marked with * are optional conditions and require a proper condition to pass to next step to work correctly.

  • STEP
    Starts a block of step instructions.

  • STEPSKIP [n]
    Defines how many steps should be skipped after finishing current step.
    Example usage: STEPSKIP 1; command for first step will make it skip second step after completion and go straight to third one.

    Defines how many steps to go behind after finishing current step.
    Example usage: STEPFALLBACK 1; command for second step will load first step after completion.

  • *NEWGAME [modname.tbm]
    Starts new game in a defined mod.
    Example usage: NEWGAME aikido.tbm; to start a new aikido.tbm match.

  • *LOADREPLAY [replayname.rpl] ?[cached]
    Loads and pauses a replay from replays/system/tutorial/ folder.
    To load replay with cache (replay speed instantly available), set 1 as cached value.
    Example usage: LOADREPLAY myreplay.rpl 1; will load a replays/system/tutorial/myreplay.rpl replay with cache.

    Enables user camera controls.
    Camera controls are also always force-enabled once the tutorial is finished.

    Disables user camera controls.

  • *DAMAGE [n]
    Sets a damage requirement to proceed to next tutorial step.
    Example usage: DAMAGE 50000; will require user to gain 50K damage to proceed to next step.

  • *DAMAGEOPT [n]
    Sets an optional damage requirement. If requirements are met, sets step skip to 1.
    DAMAGEOPT is used in punching tutorial's final task when player has to get 500k points to get a prize.
    This command has to be used along with regular condition to pass to next step.

  • *DISMEMBER [jointname]
    Sets a Uke dismember requirement. Joint names must be uppercase.
    Example usage: DISMEMBER R_KNEE; will require user to dismember Uke's right knee.

  • *FRACTURE [jointname]
    Sets a Uke fracture requirement. Joint names must be uppercase.
    Example usage: FRACTURE NECK; will require user to fracture Uke's neck.

    Slides character message box from the right side of the screen.

    Hides character message box.

    Shows hint message box.

    Hides hint message box.

    Shows tasks box.

    Hides tasks box.

    Enables advanced tooltip (doesn't affect user's settings).

    Hides advanced tooltip (doesn't affect user's settings).

    Enables "press to continue" button (if disabled).

    Hides "press to continue" button.

  • *TASK [taskname]
    Adds a task with checkbox to tasks box. Task name should correspond to a string defined in localization file.

    Marks current main task as completed.

  • TASKOPT [id] [opttaskname]
    Adds an optional task with checkbox to tasks box. Task name should correspond to a string defined in localization file.

    Marks optional task with specified id as completed.

  • TASKADD [id] [addtaskname]
    Adds an optional task without checkbox to tasks box. Task naem should correspond to a string defined in localization file.

  • *MESSAGE [stringname]
    Displays a hint message (if no message author is defined in current step) or character's mesasge.

  • SAY [username]
    Sets message author for current step.
    Use PLAYER as author name to display current user's name.

    Moves tutorial progress bar.

  • *DELAY [seconds]
    Sets delay in seconds.
    Note: you should never use this for running/pausing replays as this hugely depends on end user's computer performance.

    Sets a requirement for user to win the fight.

    Enters edit replay mode.

  • *PLAYFRAMES [frames]
    Plays specified number of frames.

  • MOVEPLAYER [player] [jointname] [jointstate]
    Changes Tori's or Uke's joint states. All variables must be uppercase.
    player can be either TORI or UKE.
    jointstate can be FORWARD, BACK, HOLD or RELAX.
    You can also use MOVEPLAYER [player] RELAXALL or MOVEPLAYER [player] HOLDALL to mass-change joint states.
    Example usage: MOVEPLAYER UKE L_PECS FORWARD; will make Uke contract left pecs.

  • *MOVEJOINT [jointname] [jointstate]
    Requires user to change joint state on Tori's body and shows a joint pulsing animation.
    Example usage: MOVEJOINT L_PECS FORWARD; will require user to change their character's pecs state to "Contracting".

  • *MOVEJOINTOPTIONAL [jointname] [jointstate] ?[taskid]
    Sets an optional joint state change requirement. Can be tied to an optional task.
    Example usage: MOVEJOINTOPTIONAL R_PECS FORWARD 1; will set an optional requirement for user to move their right pecs forwards. When requirement is met, will also mark optional task with id 1 as completed.

    Requires user to press continue button to proceed to next step.
    Button is only made clickable after all other requirements for current step are met.

    Disables mouse controls for joints (unless they are required move by MOVEJOINT).

    Enables mouse controls for joints.

    Disables keyboard controls.

    Unlocks specified keyboard keys.
    Example usage: KEYBOARDUNLOCK WASDZXC ; enables camera, ZXC joint controls and spacebar.

    Unlocks Right Shift and Left Shift keys.

  • OPT [optname] [value]
    Sets option values. Same as /opt chat command.
    Values are automatically reverted back to user values once tutorial ends.

  • PLAYSOUND [id]
    Plays a sound with corresponding id. Uses user's custom sounds if present.

  • *FAILFRAME [frames]
    Restarts current step once the fight reaches specified length in frames.
    Uses STEPFALLBACK value if specified.

  • *PROCEEDFRAME [frames]
    Moves on to next step once the fight reaches specified length in frames.
    Uses STEPSKIP value if specified.

  • GHOSTMODE [mode]
    Sets ghost mode.
    Mode values: ALL for both player's ghosts, TORI for player's ghost only, NONE for no ghosts.

  • CUSTOMFUNC [funcName]
    Runs a custom lua function from tutorialname.lua file.

Using custom Lua functions

As the title suggests, you can use custom lua code within your tutorials.
To do that, you first need to create a [mytutorialname].lua file in same location as other tutorial data files.
This file is loaded with loadfile() on tutorial start; any functions you want to run with .dat file should be put in a global functions table:
local function myFunction(viewElement, reqTable)
	echo("Hello world")

functions = {
	MyFunc = myFunction
viewElement and reqTable arguments are always passed to your function, though you aren't forced to use them. viewElement is a UIElement object that exists while current step is active and is destroyed once it's over. reqTable is step requirements table which holds all requirements to move on to next step (or fall back).
By using reqTable, you can add custom requirements or modify existing requirements.
There are also two hook namespaces that may come in handy: tbTutorialsCustom and tbTutorialsCustomStatic . First one is used for hooks that you only want to run while current step is active, second one will keep hooks running until tutorial ends.

Tutorials, UIElement and UIElement3D classes will be always available for your custom lua code, but if you want to use functions from any other classes/files make sure to include them separately (unless you're 100% sure they're already loaded, but an extra require() won't hurt).
Tutorials class itself is defined in data/script/tutorial/tutorial_manager.lua. If you're looking for more examples of how custom lua code can be used for tutorials, check existing game tutorials (data files are located in data/tutorials and custom lua code for them is located in data/script/tutorial/data).

Running your tutorial

Alright, so you got your files in one place, now you only need to run it as an actual whole thing.
There's no way to load a custom tutorial with one single command with what's included in Toribash now, but here's a quick lua script I wrote that will do it for you: runTutorial.lua
To launch your tutorial using it, put the data files in data/script/mytutorials/ folder, and then run a /ls runTutorial.lua [tutorialname] command in game. If you made everything right, your tutorial should now be running!
Last edited by sir; Aug 21, 2019 at 08:14 AM.
Sounds like a very cool feature, however I wonder if it will actually be used considering all the steps involved in creating one tutorial. Hopefully in the future we can have more automated and easier system.
Originally Posted by Smaguris View Post
Sounds like a very cool feature, however I wonder if it will actually be used considering all the steps involved in creating one tutorial. Hopefully in the future we can have more automated and easier system.

Next step is AI writing code on its own.

...but seriously, how can it get easier than this? In past you had to write a complete lua script on your own, now you can make a completely operational tutorial with cutscenes and other stuff without writing any single line of actual code.
I feel like the players that do not use forums will most likely stay unaware or uninterested in such feature. It also requires quite a few steps that I believe could be avoided.

Ideally it would be awesome to see this somehow integrated into the game itself, even if it's still just a text editor. For example, you go into "create tutorial" section, there you can "create new" (or "edit existing tutorial") and have an in-game text editor with all of the available commands displayed on the side or something.

artist's rendition

Then clicking a "create tutorial" button would automatically create the two required files, as well as import all the text into them. It could also give a shortcut to importing by suggesting to download the runTutorial.lua for easy import, or if the script is already available then simply import it straight away for a preview of the creation.

As a cherry on the top, a tutorial hub, kinda like replay sharing, would be amazing addition as that would actually make for great sharing platform, which would incentivise users to create tutorials in the first place.

Just to be clear: in no way am I saying that this new feature is bad or useless, I'm simply suggesting how to make it even more accessible and user-friendly.
Last edited by Smaguris; Jun 17, 2019 at 03:40 PM.
a GUI would be the way to go to make creating in-game tutorials a lot easier, i can't imagine there would be demand for it though

and if it is accessible to make it'll probably just be a lot of opener tutorials
If no one has done this. I will be the first.
I'm not sure if I'm missing something obvious but how would you have a replay play endlessly until the user is ready to enter the edit mode.
What do i do for grips?
What do i do for chest as well?
Last edited by footlox; Aug 21, 2019 at 03:52 PM. Reason: <24 hour edit/bump
Leader of the Spyder House
Toribash Martial Artists
See the OP for joint states, there's no right / left state (instead use FORWARD / BACK)
Grip requirements aren't present in current version of tutorials manager, but you can write a custom lua function to handle that and then call it when needed with CUSTOMFUNC.

I'm not sure if I'm missing something obvious but how would you have a replay play endlessly until the user is ready to enter the edit mode.

You'll also need a custom lua function for that. Quick example of what can be used:
lua code:
add_hook("draw2d", "tbTutorialsCustom", function()
local ws = get_world_state()
if (ws.match_frame >= ws.game_frame) then
-- TUTORIAL_LEAVEGAME controls leave_game hook behavior which is triggered on replay/fight end but is also used to exit tutorial ui on ESC press
As a programmer, I can see how much work was involved into creating this.
Congratulations and hope many people will use this feature.
I will. I have a partial tutorial written but I broke my laptop so I can't really it test it until i get my new one.
Leader of the Spyder House
Toribash Martial Artists