The road to HEXROLL 2e: introduction to SCROLL

February 2023 ยท 7 minute read

I took last week off from my day job and spent some time working on HEXROLL|2. One of the things I managed to (sort-of) complete is Scroll, a new random generation language for HEXROLL that will replace the current set of YAML model files.

Scroll offers an efficient syntax for coding text generation models and it enables most of the new capabilities planned for HEXROLL|2.

Some the capabilities Scroll has are:

Basic Scroll Tutorial

If you are a patron of Hexroll, you can follow along using the REPL editor linked in our Patreon page.

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 are 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 and we will use it frequently to generate random values and entities moving forward.

The ! operator must be used any time we wish to use an attribute to output text from a renderer or use an attribute from 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 braces notation {{greeting}} to output the random value rolled for greeting.

Finally, both greeting and the HTML renderer are placed inside the { curly braces } 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 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

Simple strings can be simply assigned into attributes using the = operator:

attribute_name = A simple string

Strings that has 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
]

List 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>
}

This is it for this basic introduction. I will cover more advanced capabilities of Scroll in a future post, but in the meantime, Enjoy the crawl,

Ithai