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