Skip to content

Scroll Tutorial - Part II

In the previous part, we covered the basics of using SCROLL to generate a random hierarchy of entities together with their attributes:

  • Defining classes of entities
  • Defining class attributes of various types
  • Rolling values from a list
  • Generating a list of entities
  • The basics of using templates to generate text

In this part we will explore:

  • Collecting and reusing generated entities
  • Using data injection to link entities together
  • Using scope references to access ancestor entity values

Linking Entities

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 hooks 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 of their depth.

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

Let's extend 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 reuse 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's 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 to do this is by 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}}'s 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 two new faction attributes, realm_name and realm_class, and used 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 to: <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>
}