Scroll Tutorial - Part I
Scroll is a domain-specific language, designed specifically for random text generation, and it enables most of the features HEXROLL 2E has.
Some of the capabilities Scroll has are:
- Rolling random values from lists.
- Rolling random values using dice notations.
- Using entity types to reuse generation rules.
- Defining global constants to reuse lists or static values.
- Creating a directed-acyclic-graph of entities that represent complex constructs (such as a complete sandbox).
- Sharing data between entities using entity collections and data injection.
Basic Scroll Tutorial
Note
You can follow along using the Scroll REPL editor at scroll.hexroll.app.
Hello Scroll
Let's begin with a simple example:
main {
greeting! @ [
* Hello
* Hey
* Hi
]
<html%
<p> {{greeting}}, Scroll. </p>
%html>
}
The basic construct in Scroll is classes. Classes are used to define entity types. In the above example, we define a class named main
that has an attribute named greeting
and an HTML
renderer.
Let's examine the greeting
attribute definition:
greeting! @ [
* Hello
* Hey
* Hi
]
This expression means: "Define an attribute named greeting
and make it visible to any renderer (using the !
symbol), then roll (@
) a random value from a list of possible values indicated by *
bulleted items inside matching [
square brackets ]
."
The @
operator is a key construct of Scroll. It is how we tell Scroll to roll random values and entities.
The !
operator must be used any time we want to export an attribute to a renderer or another entity (more on this later).
Now let's examine the HTML
renderer:
<html%
<p> {{greeting}}, World. </p>
%html>
Any entity type we define can have a <html%
%html>
block that can hold HTML template code. This code is a mixture of standard HTML tags together with template code that can reference attribute values or even execute custom logic.
In our example, we render a paragraph using the <p>
tag and then use the double curly brackets notation {{greeting}}
to output the random value rolled for greeting
.
Finally, both greeting
and the HTML
renderer are placed inside the {
curly brackets }
of the main
class. Every Scroll script must have exactly one main
class definition as the entry point for the generator.
Classes
Classes in Scroll are defined using an identifier, followed by a curly-braced block containing the class content:
Realm {
name @ [
* Ishabor
* Zeratha
* Inaba
]
}
Classes can extend other classes:
Kingdom (Realm) {
title! = "Kingdom of {{name}}"
}
Let's put this together:
Realm {
name @ [
* Ishabor
* Zeratha
* Inaba
]
}
Kingdom (Realm) {
title! = "Kingdom of {{name}}"
}
main {
realm! @ Kingdom
<html%
Our realm is the
<strong>{{realm.title}}</strong>.
%html>
}
In the above example, we've defined a base Realm
type with a random name. We then extended this class by defining a new class named Kingdom
and indicating that it will be extending Realm
using the round parentheses notation Kingdom (Realm) {...}
.
In the main
class, you can see how we now use the @
operator to roll an entity by using a class name.
Finally, note how we do not use the !
symbol for the name
attribute since it is not directly used in the HTML rendering code. It is important to use !
selectively, especially when generating a large number of entities.
Let's take this a step further:
Realm {
name @ [
* Ishabor
* Zeratha
* Inaba
]
}
Kingdom (Realm) {
title! = "Kingdom of {{name}}"
}
Duchy (Realm) {
title! = "Duchy of {{name}}"
}
main {
realm! @@ [
* Kingdom
* Duchy
]
<html%
Our realm is the
<strong>{{realm.title}}</strong>.
%html>
}
We've added yet another realm type named Duchy
and used the double roll operator @@
to roll a value from a list, and then used that value as the class name to roll an entity from.
Attributes
We've already seen how we can define attributes in classes. We've used the =
operator to assign a value to an attribute, and we've used the @
operator (the @@
variant) to roll a random value into an attribute.
There are two additional assignment operators that can be used to choose entities from entity collections. I will cover these in a later post.
Scroll attributes can contain the following data types:
- Strings
- Numbers
- Dice roll values
- Boolean values (true or false)
- Entities
- Lists of entities
Strings
Simple strings can be simply assigned into attributes using the =
operator:
attribute_name = A simple string
Strings that have special symbols require surrounding quotes:
attribute_name = "a string with {{template}} code"
Or surrounding <
>
symbols if they span across multiple lines:
attribute_name = <a longer
multi-line string>
Or surrounding <%
%>
symbols if they span across multiple lines and contain special symbols:
attribute_name = <% a longer multi-line string
with {{template}} code and other
<symbols> %>
Rolling random values from lists is done using the @
operator. List items can span across multiple lines as well.
attribute_name @ [
* lists can have multi-line strings
specified without the < > notation.
* < Unless the text contains the * symbol >
]
Numbers
attribute_name = 42
attribute_name = 42.0
Dice roll values
Scroll allows rolling dice values using the @
operator, followed with a dice roll notation:
attribute_name @ 3d6
attribute_name @ 1d20+2
attribute_name @ 8d7x2
Booleans
attribute_name = true
attribute_name = false
Entities
As demonstrated in the examples above, entities can also be rolled using the @
or @@
operators:
attribute_name @ EntityClassName
attribute_name @@ [
* OneEntityClassName
* AnotherEntityClassName
]
Lists of Entities
Scroll has a special notation for rolling lists of entities:
[3..10 attribute_name] @ EntityClassName
[5..5 attribute_name] @@ [
* OneEntityClassName
* AnotherEntityClassName
]
Attributes that represent lists are placed inside [
square brackets ]
and prefixed with a cardinality notation of min_value..max_value
where min_value
is the minimum number of items to roll and max_value
is the maximum number of items to roll.
List items can be rendered using {% for item in list %} {{item.value}} {% endfor %}
template syntax.
Putting everything together
Let's conclude this short introduction with the following example:
Npc {
first_name @ [
* Walatus
* Ajutor
* Sieggo
* Milia
* Petesia
* Lefsy
* Latilde
]
last_name @ [
* Bossinia
* Brightmer
* Ermenbald
* Hildeward
* Siclebald
* Zawissius
]
age @ 7d12
description! = < {{first_name}} {{last_name}}
({{age}} years old) >
}
Settlement {
name @ [
* Akkis
* Bazuul
* Devilville
* Ecrean
* Frostfyord
* Khezal
]
<html%
{{title}} is home for: <ul>
{% for npc in npcs %}
<li> {{ npc.description }} </li>
{% endfor %} </ul>
%html>
}
Village (Settlement) {
title! = "The Village of {{name}}"
[6..12 npcs!] @ Npc
}
Town (Settlement) {
title! = "The Town of {{name}}"
[12..33 npcs!] @ Npc
}
Realm {
name @ [
* Ishabor
* Zeratha
* Inaba
]
[3..6 settlements!] @@ [
* Town
* Village
]
}
Kingdom (Realm) {
title! = "Kingdom of {{name}}"
}
Duchy (Realm) {
title! = "Duchy of {{name}}"
}
main {
realm! @@ [
* Kingdom
* Duchy
]
<html%
<p>
Our realm is the
<strong>{{realm.title}}</strong>.
</p>
<p>
The realm has the following settlements:
{% for s in realm.settlements %}
<p> {{html(s)}} </p>
{% endfor %}
</p>
%html>
}
To learn more, visit part two of the tutorial and learn how you can use collections, data injection and context reference to link entities together.