Making more dynamic stories
In this part of the story.kts guide, we will explore how to make more dynamic stories.
Kotlin knowledge
For this part, it is recommended that you know at least a bit of Kotlin.
This page will cover most of the things you need to know for now. Try to understand this page from “Defining variables” to “Using conditional expressions”. It is recommended to read the entire page to have a fuller understanding of the Kotlin syntax.
Using variables
The easiest way to have variables in your program is by using Kotlin’s var
.
Here is a simple example of how you add variables:
var myVariable = 33
story {
...
}
Now, if we want to use this variable, we can simply use Kotlin’s string template feature.
var myVariable = 33
story {
node(1) {
body {
"""
This is $myVariable
"""
}
...
}
...
}
Once you open the story, you will see “This is 33”. Since this is a variable,
we can also change it. The does
block of an option is a good place to do that.
var myVariable = 33
story {
node(1) {
body {
"""
This is $myVariable
"""
}
option { "Add one" } does {
myVariable = myVariable + 1
null
}
option { "Remove one" } does {
myVariable = myVariable - 1
null
}
}
}
Because the does
block always needs to provide a node where the option should
lead, we use null
to indicate that we simply want to lead back to the current
node, that we don’t want to move.
Now, if we load the story, we have two options to increase and decrease our variable.
Note that Kotlin is a statically-typed language, meaning that myVariable will have the same type all the time. Here, it is an integer. It will thus remain an Integer. For example, this is not possible:
var myVariable = 1
myVariable = "Hello!"
Another spot where it might be appropriate to change our variable is right when
we reached the node. We can do this using a onNodeReached
block.
var wasMyVariableReached = false
story {
node(1) {
body {
"""
Let's go!
"""
}
option { "Alright!" } does { nodeRef(2) }
}
node(2) {
body {
"""
It's $wasMyVariableReached
"""
}
onNodeReached {
wasMyVariableReached = true
}
}
}
The onNodeReached
block is called after the option’s does
block but before
the body
block. When we click on the option from the first node:
- The
does
block is called and tells us to go to the node which has the id 2 - The
onNodeReached
block is called. It setswasMyVariableReached
to true - The
body
block is called to determine the text of the node, and, since at this pointwasMyVariableReached
is true, it will display “It’s true”
Storing stuff in the environment
The “environment” is a generic term for “where your story lives”. The environment has a lot of information, but one of its most interesting aspects is environment delegation.
Later, we will see how to have multiple stories from completely different files and how to jump from one to the other.
var
iables only exist within the file where you declare them. Because of this,
if you have two files like so…
// File 1
var myVariable = 20
story {
id = "main"
node(1) {
body {
"""
International travel?
"""
}
option {"Heck yes!"} does {
myVariable = 300
ref("other", 50)
}
}
}
// File 2
story {
id = "other"
node(50) {
body {
"""
What is my variable? :(
"""
}
}
}
… the second file has no way of knowing what myVariable
is, nor can it
modify it in a way that would be visible to the first file.
This is where environment-delegated properties come into action! Instead of storing our variable “in the file”, we can store it “in the environment”, making it available for all the stories ran under the same environment. The only restriction is that, in order to be “linked”, the variables need to share the same name.
// File 1
var myVariable by env.delegated(20)
story {
...
}
// File 2
var myVariable: Int by env.delegated()
story {
id = "other"
node(50) {
body {
"""
My variable is $myVariable!
"""
}
}
}
Notice that, in the first file, the way we provide a default value (i.e. a value
if the environment has never received anything here), is by passing an argument
to the delegated
call.
Note that the default value is not shared among multiple delegated variables, meaning that you could have an environment variable at two places with the two spots giving different default values. If this happens, the default value used is the one from the variable that gets used first.
If you do not provide a default value, like in File 2:
- You have to explicitly specify the type of the variable: it is an integer here
- You have to make sure that you never use the variable before it is initialized
- Either set it before doing anything with it
- Or be sure that another file is setting it for you