- This is a tool for randomly generating characters (NPCs or PCs), monsters - and just about anything that could be modelled in a RPG system.
- Utilizing templates, lookup tables, and a random number generator, it is possible to automatically generate many of the entities that are present in conventional tabletop roleplaying games.
- There are many fun and exciting roleplaying game systems. Some of these systems provide more detail and 'crunch' than others. The downside of 'crunch' is that it can distract from the narrative flow of a game session.
- In the worst case, the work involved in supporting such a detailed set of rules causes a game to become a drab and boring exercise in bookkeeping - a 60-second combat takes three hours to resolve, players get bored waiting for the GM to roll up new NPCs, etc.
- This tool is intended to make generating entities of any kind - NPCs, creatures, villages, vehicles, dungeons, etc quick and easy, so that the focus of the game remains on the story, but without having to give up the lush detail that a crunchy ruleset provides.
- Templates are defined in plain text .yaml format. Like any yaml document, a template has a tree structure. Each node has a value, which may either be a bit of text, or a subtree of nodes.
- Template functionality is a little bit like that provided by XSLT/XPath (but much more limited), and allows for
- virtual random dicerolls
- external table lookup values
- references to specific values within the same template
- However, namespaces and looping constructs are not supported.
- Template syntax is yaml syntax (see http://www.yaml.org ), plus a few special escaped sequences
-
Every template starts with the same structure - a root
templatenode, and a childtemplate_namenode:template: template_name: the_name_of_the_template -
The rest of the template is comprised of nodes that hang off the root
templatenode -
In the value portion of a yaml template key-value pair,
- everything between two escape sequences
/% /%is dynamically interpreted (see syntax, below). - everything outside of
/% /%is treated as literal string values.
- everything between two escape sequences
-
You can have any number of escaped regions within a single value, and they may be interspersed with static text.
- Within each
/% %/escape sequence pair, you can include special directives:- one or more diceroll specifications e.g.
1D6, which will automatically be resolved to an actual number using a random number generator - one or more table lookups
- one or more references to other nodes in the template. Such references are materialized on-demand.
- an arithmetic expression e.g. 4 * 27
- one or more diceroll specifications e.g.
- Within each
-
-
Diceroll Specification Directive
-
A diceroll specification is of the form
number of diceDnumber of sides -
Note that there are no spaces between the numbers and the
D -
Also, the
Dmay be upper- or lower-case (d) -
Any number of sides is allowed - since these are virtual dice, you could specify a 13-sided die…or even a 2,521-sided die(!)
-
For example, here, we roll 3 6-sided dice to get the
STRattribute value:STR: \% 3d6 \%
-
-
Table Lookup Directive
-
Lookup Tables
- Like templates, tables are defined in plain text .yaml format
- Tables may be one- or two-dimensional (using either just a Y-index lookup, or both a Y- and an X- index lookup)
- Tables may use ranges of values; this results in slower lookups, but can save a lot of work in typing the actual table file
-
One-Dimensional Lookup Tables
-
A one-dimensional table is a table that is referenced by a single value, e.g. you would use Age to look up the IQ value from this table:
Age IQ 16 108 17 108 18 105 19 105 20 99 21 99 22 99 25 97 26 97 27 97 28 97 29 97 30 97 31 97 32 97 33 97 34 97 35 101 36 101 -
If we wanted to call this table "IQbyAge", we would implement the table in YAML like this:
table: name: IQbyAge y-axis: number entries: 16: 108 17: 108 18: 105 19: 105 20: 99 21: 99 22: 99 25: 97 26: 97 27: 97 28: 97 29: 97 30: 97 31: 97 32: 97 33: 97 34: 97 35: 101 36: 101 -
In a template, a one-dimensional table lookup is expressed thusly:
[the_table_name|the lookup key] -
So, for example, if we were creating a template that uses the IQbyAge table, our lookup might look like this:
IQ: \% [IQbyAge|25] \% -
Of course, using a fixed value to reference a table is fine, but not very powerful. We'll see further on how you can combine random values and other values from the template to drive table lookups.
-
-
Value Ranges
-
It can be convenient to use value ranges in tables - it's less work to create and maintain a table that uses value ranges than the equivalent table that relies on discrete values.
Age Range IQ 16–17 108 18–19 105 20–24 99 25–34 97 35–44 101 45–54 106 55–64 109 65–69 114 -
Use square brackets in the key part of each value in the yaml file to indicate that a table uses ranges e.g.
"[0-49]" -
The above table would be implemented in a .yaml file like this:
table: name: IQbyAge y-axis: number entries: "[16-17"]: 108 "[18-19"]: 105 "[20-24]": 99 "[25-34]": 97 "[35-44]": 101 "[45-54]": 106 "[55-64]": 109 "[65-69]": 114
-
-
Note that the template's lookup expression would not change from the prior example:
IQ: \% [IQbyAge|25] \% -
You still pass a single numeric value to the table.
-
-
Two-Dimensional Lookup Tables
-
A two-dimensional table is a table that is referenced by two values.
-
e.g. You would use Age and Sex to look up the IQ value from this table:
Age Male Female 16 108 109 17 108 109 18 105 106 19 105 106 20 99 100 21 99 100 22 99 99 23 98 98 24 98 97 25 97 96 26 97 95 27 97 94 28 97 95 29 97 96 30 97 97 31 97 98 32 97 99 33 97 100 34 97 101 35 101 102 36 101 103 -
this two-dimensional table would be implemented in yaml like this:
table: name: IQbyAgeAndSex y-axis: number x-axis: sex entries: 16: Male: 108 Female: 109 17: Male: 108 Female: 109 18: Male: 105 Female: 106 19: Male: 105 Female: 106 20: Male: 99 Female: 100 21: Male: 99 Female: 100 22: Male: 99 Female: 99 25: Male: 97 Female: 96 26: Male: 108 Female: 95 27: Male: 108 Female: 94 28: Male: 108 Female: 95 29: Male: 108 Female: 96 30: Male: 108 Female: 97 31: Male: 108 Female: 98 32: Male: 108 Female: 99 33: Male: 108 Female: 100 34: Male: 108 Female: 101 35: Male: 108 Female: 102 36: Male: 108 Female: 103 -
two-dimensional table lookups are denoted thusly:
[table_name|lookup key Y|lookup key X] -
So, for example, the template entry for looking up IQ from the IQbyAgeAndSex table might look like this:
IQ: \% [IQbyAgeAndSex|25|Male] \%
-
-
-
-
Template Reference Directive
-
Using a template reference directive, you can incorporate the value of another node in the template
-
A reference is denoted using curly braces, and is expressed like a directory path e.g.
{template/name}. -
Let's look at this simple example template that uses a template reference directive:
template: template_name: Fighter Description: a generic fighter stats: ST: \% 3d6 \% DX: \% 2d6+6 \% IQ: \% 3d6 \% HP: \% {template/stats/ST} \%- In the above example, the
HPstat is set to the value of theSTstat using a template reference directive.
- In the above example, the
-
-
Combining Different Directives Within A Single Value
- Directives may be combined in limited ways; for example:
-
References to other values within the template may be combined as part of an arithmetic expression.
-
Here, we add the
STRandDEXstats to derive theAthleticsskill rank:Athletics: \%{template/stats/STR} + {template/stats/DEX} \% -
Here, we combine the
INTandDEXstats, and perform some additional math to derive action points:action_points : \% ((( {template/stats/INT} + {template/stats/DEX} ) -1 ) / 12 ) + 1 \%
-
-
A table lookup can use a dice roll specification, as in this example:
hair_color: \% [Hair Color|1D100] \% -
A table lookup can use another value in the template
-
Here, we use the
SIZstat to look up the height from theHuman Heighttable:height: \% [Human Height|{template/stats/SIZ}] \%
-
-
Here, we use both a template value reference directive and a random number directive within a table lookup directive to look up a name:
name: \% [Names|{template/sex}|1d4391] \%
-
- A dice roll spec can be combined with an arithmetic expression and/or another value in the template
- Some combinations are not supported.
- For instance, table lookups cannot be nested.
- However, as a workaround, you can capture an intermediate value in a template node, and then refer to that template node in another node definition…
-
Here's a simple example of using an intermediate value for a table lookup:
submarine_color: \% [Colors|1D12] \% submarine_size: \% [Size|{template/submarine_color}|1D6] \%
-
- However, as a workaround, you can capture an intermediate value in a template node, and then refer to that template node in another node definition…
- Dice roll specifications cannot be nested, either.
- For instance, table lookups cannot be nested.
- Another interesting thing you can do is to use literal yaml in your lookup tables (see the Medieval Weapons lookup table definition for an example of this technique)
- Directives may be combined in limited ways; for example:
- The templates and tables included in this repository are mostly intended for NPC/monster generation, and are based on the rules for the Mythras tabletop role-playing game.
- This tool is open-ended, however, so it should be possible to create tables and templates for most role-playing games.
- Scala 2.12 runtime
- SnakeYAML
- Jonathan Donald
- Mersenne Twister random number generation code is by jesperdj https://gist.github.com/jesperdj/887771
- The Entity Generator relies on Andrey Somov's SnakeYAML Java library to parse YAML files
- Inspiration came from various roleplaying games, especially Steve Jackson's The Fantasy Trip, N. Robin Crossby's HarnMaster, Iron Crown Enterprises RoleMaster, and The Design Mechanism's Mythras (formerly RuneQuest 6)
- Additional inspiration and ideas came from Hannu Kokko's wonderful RuneQuest 6/Mythras Encounter Generator
- This project is licensed under the GNU Lesser General Public License v3.0 - see the LICENSE file for details.