The road to HEXROLL 2e: more on SCROLL

July 2023 ยท 6 minute read

SCROLL

Follow along using the SCROLL (alpha version, bugs, no-guarantees, etc) REPL environment.

In the previous post, I covered the basics of using SCROLL to generate a random hierarchy of entities together with their attributes.

I covered:

In this post I will continue and explore:

To me, one of the neat things about HEXROLL is its ability to link entities together. This is done in a way that is just enough to prompt you with cool plot hook to further develop.

Let’s take a look at how this linking thing actually works.

Collections

By using collections we can instruct an entity to ‘collect’ references to other entities generated as part of its hierarchy, regardless to their depth.

For example, in the previous post we generated realms that contain settlements, which in turn, contain NPCs.

Let’s extended that model and use a collection directive to store references to all generated NPCs inside the realm:

Realm {
  << Npc
  name @ [
    * Ishabor
    * Zeratha
    * Inaba
  ]
  [3..6 settlements!] @@ [
    * Town
    * Village
  ]
}

We use the << operator followed by a class name to tell SCROLL to store a reference to any generated entity of that class within its hierarchy.

<< Npc

This allows us to re-use collected entities in other entities within the same parent class.

For example, we can use the collected NPCs to form factions with members from any part of the realm:

Faction {
  name! @ [
    * The Flaming Lambs
    * The Army of Justice
  ]

  [6..13 members!] ? Npc

  <html%
    <h1>{{name}}</h1>
    <h2>Members</h2>
    <ul>
    {% for member in members %}
    <li> {{ member.full_name}} </li>
    {% endfor %}
    </ul>
  %html>
}

We use the ? operator (pop entity) instead of the @ operator to ask SCROLL to use one of the referenced NPCs as a faction member.

Note that by using the ? operator, we tag any randomly selected entity as used. Alternatively, we can use the % operator (pick entity) to select a random entity from a collection rather than use it.

Data Injection

Data injection in SCROLL is a method to share data between entities - specifically when generated on different “branches” of the directed graph.

For example, we can use data injection to populate faction NPCs with their faction name:

Faction {
  name! @ [
    * The Flaming Lambs
    * The Army of Justice
  ]

  [6..13 members!] ? Npc {
    faction = &name
  }

  <html%
    <h1>{{name}}</h1>
    <h2>Members</h2>
    <ul>
    {% for member in members %}
    <li> {{ member.full_name}} </li>
    {% endfor %}
    </ul>
  %html>
}

To indicate the attributes we want to inject, we use a { curly braced } block immediately after the class name we want to reuse (Npc in this case). This tells SCROLL that for each used entity it picks or pops from a collection, it has to set additional injected values.

[6..13 members!] ? Npc {
  faction = &name
}

We then specify faction as the attribute we want to inject to and &name as the value, which means, copy the value from the owning entity attribute name. The & operator means copy in this context.

Note that there’s another way to reference attributes when injecting data by using the pointer operator * - but we will cover this in a later post.

Now that we have injected data into selected NPCs, we can use it to enrich their description:

Npc {
  first_name @ [
    * Walatus
    * Ajutor
    * Sieggo
    * Milia
    * Petesia
    * Lefsy
    * Latilde
  ]
  last_name @ [
    * Bossinia
    * Brightmer
    * Ermenbald
    * Hildeward
    * Siclebald
    * Zawissius
  ]
  age @ 7d12
  faction = 0
  description! = <%
    {{first_name}} {{last_name}}
    ({{age}} years old)

    {% if faction %}
    {{faction}}
    {% endif %}
  %>
}

First, we declared the faction attribute and set it to 0 - telling the generator that this attribute is currently holding no value. We could also use the keywords null or false.

Next, in the faction description attribute, we check whether or not faction was set externally, and if it holds a value, we use it.

Note that another way of do this is using the template function maybe, but I will cover this in the next post.

Scope References

In cases when an entity needs a value from one of its ancestors, we can use a scope reference:

Faction {
  name! @ [
    * The Flaming Lambs
    * The Army of Justice
   ]
  realm_name = :Realm.name
  realm_class = :Realm.class
  objective! = <%
    {{name}} objective is to overthrow
    {{realm_name}}'s ruler and gain control over
    the {{realm_class}}.
  %>

  [6..13 members!] % Npc {
    faction = &name
  }

  <html%
    <h1>{{name}}</h1>
    <p> {{objective}} </p>
    <h2>Members</h2>
    <ul>
    {% for member in members %}
    <li> {{ member.full_name}} </li>
    {% endfor %}
    </ul>
  %html>
}

We use the : operator followed by an ancestral class name to refer to a parent, and then use . to reference a specific attribute:

realm_name = :Realm.name
realm_class = :Realm.class

In this case, we created to new faction attributes, realm_name and realm_class, and using the parent Realm entity to set their values accordingly.

Putting everything together

Copy and paste the following into the REPL editor:

Npc {
  first_name @ [
    * Walatus
    * Ajutor
    * Sieggo
    * Milia
    * Petesia
    * Lefsy
    * Latilde
  ]
  last_name @ [
    * Bossinia
    * Brightmer
    * Ermenbald
    * Hildeward
    * Siclebald
    * Zawissius
  ]
  full_name! = <% {{first_name}} {{last_name}} %>
  age @ 7d12
  faction = 0
  description! = <%
    {{first_name}} {{last_name}}
    ({{age}} years old)
    {% if faction %}
    <br/>
    <strong>Member of {{faction}}</strong>
    {% endif %}
  %>
}

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..24 npcs!] @ Npc
}

Faction {
  name! @ [
    * The Flaming Lambs
    * The Army of Justice
  ]

  [6..13 members!] % Npc {
    faction = &Name
  }

  <html%
  <h1>{{name}}</h1>
  <h2>Members</h2>
  <ul>
  {% for member in members %}
  <li> {{ member.full_name}} </li>
  {% endfor %}
  </ul>
  %html>
}

Realm {
  << Npc
  name @ [
    * Ishabor
    * Zeratha
    * Inaba
  ]
  [3..6 settlements!] @@ [
    * Town
    * Village
  ]
  faction! @ Faction
}

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(realm.faction)}}
  %html>
}

The next post on SCROLL will be dedicated to template functions and how they can be used to process entity values when rendering HTML, as well as to some of the less frequently used features of the language. Until then.. Enjoy the Crawl!

Ithai