I posted my Index of Allowed Classes, Races, Cultures, and Backgrounds on Reddit. Someone asked me:
Am I missing something here? This is just a list of various races and subclasses for use in your game? Maybe something about why you chose these would be good.
Why the index? And why these?
First the question of why the index.
As a game master, I assume I have gathered more gaming material than my players. And I want to draw attention to options that I’ve found that might be interesting. I would love to see someone play a Bone Knight or Divine Herald from Morgrave Miscellany.
Second, looking at the site stats, my (now neglected) Dungeon World playbooks continues to remain quite popular. I figure, why not do something similar for Dungeons and Dragons: Fifth Edition (5E 📖) Dungeons and Dragons.
And why did I choose these?
To show my players what they can pick from, I needed to start somewhere. I picked through the books I had available. I intend to add to the index as things catch my eye. I had wanted to use the Sword Coast Adventure Guide, but it appears I’ve either sold it or misplaced it.
A Technical Curiosity
With that in mind, I also wanted to write some code. When I first started the index, I wanted to emulate the data driven aspect of my Burning Wheel lifepaths inspired by Warhammer Fantasy. I store those lifepaths in a Yet Another Markup Language (YAML 📖) file and use a Hugo shortcode to render the Hypertext Markup Language (HTML 📖).
I outlined the following steps:
- Create a spreadsheet for the index
- Convert the spreadsheet to YAML
- Create a shortcode to render the YAML
In an ideal world, I would have an OAuth2 script to download the spreadsheet via an Application Programming Interface (API 📖), then process accordinly. However, I chose to manually download the Comma Separated Value (CSV 📖) file.
Below is my Rake (Rake 📖) (Ruby language) task for converting CSV to YAML.
Rake task to convert CSV to YAML
namespace :csv do
desc "Given the data CSVs, convert them to YAML for dynamic rendering"
task :convert_to_yaml do
require 'csv'
require 'psych'
label_lookup_table = {
"class_name" => "Class",
"subclass" => "Subclass",
"background_name" => "Background",
"source_name" => "Source",
"race_name" => "Race or Culture",
"race_subtype" => "Subtype"
}
[
{ basename: "classes", name: "Classes and Subclasses" },
{ basename: "backgrounds", name: "Backgrounds" },
{ basename: "races-and-cultures", name: "Races and Cultures" }
].each do |file_data|
basename = file_data.fetch(:basename)
name = file_data.fetch(:name)
rows = []
columns = []
data = {
"name" => name,
"columns" => columns,
"rows" => rows
}
CSV.foreach(File.join(PROJECT_PATH, "tmp/#{basename}.csv"), headers: true) do |csv|
if columns.empty?
csv.headers.each do |header|
next if header == "source_url"
columns << { "key" => header , "label" => label_lookup_table.fetch(header, header) }
end
end
row = {}
columns.each do |column|
key = column.fetch("key")
row[key] = csv[key]
end
if csv["source_url"]
row["source_name"] = "[#{csv["source_name"]}](#{csv["source_url"]})"
end
rows << row
end
File.open(File.join(PROJECT_PATH, "data/eberron/#{basename}.yml"), 'w+') do |f|
f.puts Psych.dump(data)
end
end
end
end
From the YAML, I use a generic data_table Hugo template to build the tables.
Hugo `shortcode` to process dynamic table
{{- $scope := .Get "scope" }}
{{- $container_name := .Get "container" }}
{{- $collapse := .Get "collapse" }}
{{- $tableNumber := .Page.Scratch.Get "tableNumber" }}
{{- if eq $tableNumber nil }}{{ .Page.Scratch.Set "tableNumber" 0 }}{{ end }}
{{- .Page.Scratch.Add "tableNumber" 1 }}
{{- $tableNumber = .Page.Scratch.Get "tableNumber" }}
{{- $container := index $.Site.Data $container_name }}
{{- with index $container $scope }}
{{- if $collapse }}
<details>
<summary id="{{ anchorize $scope }}-dom-id">{{ .name }}</summary>
{{- else }}
<h2 id="{{ anchorize $scope }}-dom-id">{{ .name }}</h2>
{{- end }}
<div class="table-wrapper">
<table class="data-tables stripe" aria-label="{{ .name }} Progression">
<caption>Table {{ $tableNumber }}: {{ .name | markdownify }}</caption>
<thead>
<tr>
{{- $columns := .columns }}
{{- range $columns }}
<th scope="col">{{ .label }}</th>
{{- end }}
</tr>
</thead>
<tbody>
{{- range .rows }}
{{- $data := . }}
<tr>
{{- range $columns }}
<td>{{ index $data .key | markdownify }}</td>
{{- end }}
</tr>
{{- end }}
</tbody>
</table>
</div>
{{- if $collapse }}
</details>
{{- end }}
{{- end }}
And how to render the shortcode:
data_table container="eberron" scope="classes" collapse="true"