WEBVTT captioned by sachac, checked by sachac
NOTE Introduction
00:00:00.000 --> 00:00:05.559
Hi there, I'm Howard Abrams. You may remember me
00:00:05.560 --> 00:00:07.719
from past conference talks
00:00:07.720 --> 00:00:10.519
as "Literate DevOps and the Temple of Doom"
00:00:10.520 --> 00:00:13.399
and "Using Eshell for Fun and Profit".
00:00:13.400 --> 00:00:16.599
I'm here to talk to you about my latest Emacs project:
00:00:16.600 --> 00:00:19.479
playing games, solo role-playing games.
00:00:19.480 --> 00:00:23.159
I started playing RPGs when I got my first copy
00:00:23.160 --> 00:00:25.599
of Dungeons & Dragons when I was 12.
00:00:25.600 --> 00:00:28.279
Yes, my original copy burned
00:00:28.280 --> 00:00:30.559
in the Great Satanic Panic of the 1980s,
00:00:30.560 --> 00:00:32.359
but that's another story.
00:00:32.360 --> 00:00:37.919
I started playing other RPGs like GURPS.
00:00:37.920 --> 00:00:39.999
These are some of my notes.
00:00:40.000 --> 00:00:42.559
Back then, I was typing them in Emacs,
00:00:42.560 --> 00:00:46.079
but I formatted them with LaTeX.
00:00:46.080 --> 00:00:49.079
Later, when I was introducing my kids
00:00:49.080 --> 00:00:50.839
to role-playing games,
00:00:50.840 --> 00:00:53.580
I actually typed them up still in Emacs,
00:00:53.581 --> 00:00:57.599
but now formatted them for a tablet.
00:00:57.600 --> 00:00:59.319
I wrote a little JavaScript code
00:00:59.320 --> 00:01:03.119
that allowed me to click on it, and it would roll dice,
00:01:03.120 --> 00:01:06.679
generate random events, keep track of turn order,
00:01:06.680 --> 00:01:07.479
you know, everything,
00:01:07.480 --> 00:01:10.119
so I didn't have to slow down the action of the game.
00:01:10.120 --> 00:01:12.999
Well, when my kids got older,
00:01:13.000 --> 00:01:15.599
I still managed to sneak in a game of D&D
00:01:15.600 --> 00:01:17.319
once a week at lunch.
00:01:17.320 --> 00:01:20.679
This pastime came to a screeching halt with the pandemic.
NOTE Solo RPGs
00:01:20.680 --> 00:01:23.639
I turned to playing role-playing games by myself
00:01:23.640 --> 00:01:27.999
to get my fix. Playing these silly elf games in solo mode
00:01:28.000 --> 00:01:29.879
has been part of the game for many years,
00:01:29.880 --> 00:01:32.559
but with so many of us stuck at home,
00:01:32.560 --> 00:01:35.119
solo role-playing games really expanded,
00:01:35.120 --> 00:01:40.279
creative people releasing some amazing ideas.
00:01:40.280 --> 00:01:44.399
What's a solo RPG like? Well, it's somewhere in the middle
00:01:44.400 --> 00:01:47.519
of writing your own story, where anything's possible,
00:01:47.520 --> 00:01:50.159
but you've got to do all the imaginative work;
00:01:50.160 --> 00:01:52.999
or reading a choose-your-own-adventure book,
00:01:53.000 --> 00:01:55.239
where the text is given to you,
00:01:55.240 --> 00:01:59.079
and you have free, a few predetermined paths;
00:01:59.080 --> 00:02:01.039
and tactical battle games,
00:02:01.040 --> 00:02:03.159
where dice determines everything.
00:02:03.160 --> 00:02:05.799
It kind of fits in the sweet spot between those.
00:02:05.800 --> 00:02:08.879
While I started removing the Game Master
00:02:08.880 --> 00:02:12.119
using the Mythic GM Emulator,
00:02:12.120 --> 00:02:15.319
Ironsworn really captivated me.
00:02:15.320 --> 00:02:19.199
I began with dice, pencils, notebooks, you know,
00:02:19.200 --> 00:02:23.359
just like when I was a kid. But taking notes on paper?
00:02:23.360 --> 00:02:27.999
Yeah, you know me. That's not my jam. Org mode is.
00:02:28.000 --> 00:02:31.159
And, you know, notes have to be in Org,
00:02:31.160 --> 00:02:35.159
well, why not write a little dice roller in Lisp?
00:02:35.160 --> 00:02:38.799
Well, when Shawn Tomkin released his Ironsworn
00:02:38.800 --> 00:02:41.879
under the Creative Commons, well,
00:02:41.880 --> 00:02:43.919
I could just download the entire text.
00:02:43.920 --> 00:02:47.439
I figured I could just render the entire game in Emacs.
NOTE Demo
00:02:47.440 --> 00:02:51.239
All right, enough talk. Let's get some Emacs action here,
00:02:51.240 --> 00:02:55.199
while I show you a bit of my game.
00:02:55.200 --> 00:02:57.519
When playing a solo RPG,
00:02:57.520 --> 00:02:59.759
I jot down the story notes in an Org file.
00:02:59.760 --> 00:03:02.759
I mean, did you expect anything less from me?
00:03:02.760 --> 00:03:07.759
I alternate between lengthy prose and short notes.
00:03:07.760 --> 00:03:10.519
As I'm both the writer and the audience,
00:03:10.520 --> 00:03:11.999
the goal is just enjoyment.
00:03:12.000 --> 00:03:16.999
So, this document is both a record log of my game sessions,
00:03:17.000 --> 00:03:20.959
as well as my character's character sheet.
00:03:20.960 --> 00:03:24.519
In most RPGs, a player's focus is a character sheet
00:03:24.520 --> 00:03:26.999
that lists all the attributes, the stats, equipment,
00:03:27.000 --> 00:03:28.759
powers, you know, that sort of thing.
00:03:28.760 --> 00:03:32.959
For my game, I wanted the focus to be the prose,
00:03:32.960 --> 00:03:34.559
or at least the notes.
00:03:34.560 --> 00:03:38.199
So, I put down all the stats as Org mode properties.
00:03:38.200 --> 00:03:40.799
Now, I can collapse a property drawer
00:03:40.800 --> 00:03:42.119
and have functions
00:03:42.120 --> 00:03:45.759
that just grab values from these properties.
00:03:45.760 --> 00:03:50.079
All right, let's play. While not important to my talk,
00:03:50.080 --> 00:03:52.679
I'm in the middle of a game. My character, Tegan,
00:03:52.680 --> 00:03:54.959
promised to help a village by tracking down
00:03:54.960 --> 00:03:59.239
the son of a village chief. A less-than-stellar roll
00:03:59.240 --> 00:04:01.199
meant I didn't catch him before he entered
00:04:01.200 --> 00:04:03.879
the mysterious underground structure
00:04:03.880 --> 00:04:06.399
of a relic of an ancient people.
00:04:06.400 --> 00:04:08.399
I just finished playing out the journey,
00:04:08.400 --> 00:04:11.759
and he's about to enter into the Catacombs of Svala's Blood.
NOTE Randomization
00:04:11.760 --> 00:04:15.199
Why that name? Well, that was actually what came up
00:04:15.200 --> 00:04:19.639
from an extensive random number generator that I wrote.
00:04:19.640 --> 00:04:21.959
As I wrote more and more functions
00:04:21.960 --> 00:04:23.279
to help me play this game,
00:04:23.280 --> 00:04:25.919
and since I don't play all the time,
00:04:25.920 --> 00:04:30.359
I created hydra. I can roll dice,
00:04:30.360 --> 00:04:34.079
I can roll dice challenges against the character stats,
00:04:34.080 --> 00:04:38.199
I can adjust stats. Lots of random generators
00:04:38.200 --> 00:04:39.479
come from this oracle section.
00:04:39.480 --> 00:04:43.159
For instance, are footprints going through the door?
00:04:43.160 --> 00:04:46.479
I press `c`, and I'm prompted with how likely.
00:04:46.480 --> 00:04:51.079
Since the villagers gave Tegan vague directions,
00:04:51.080 --> 00:04:53.239
and he didn't see any signs the contrary,
00:04:53.240 --> 00:04:58.479
I chose "likely". And, well, it originally said yes,
00:04:58.480 --> 00:05:01.599
and that's why I jotted this information down.
00:05:01.600 --> 00:05:03.479
Now, this is different than my character's ability
00:05:03.480 --> 00:05:07.639
to notice the prints. This is about generating the story,
00:05:07.640 --> 00:05:10.279
something that the game master would do
00:05:10.280 --> 00:05:12.479
in a typical role-playing game.
00:05:12.480 --> 00:05:14.719
Now, if I wanted to name something,
00:05:14.720 --> 00:05:16.039
or even the current weather,
00:05:16.040 --> 00:05:20.399
I have random tables with the `C` keystroke.
00:05:20.400 --> 00:05:27.279
Hmm, weather. Oh, it's summer, so hey,
00:05:27.280 --> 00:05:31.959
it's nice and clear. All right, let's play.
NOTE Moves
00:05:31.960 --> 00:05:34.239
The action in Ironsworn,
00:05:34.240 --> 00:05:37.039
like other Powered by the Apocalypse games,
00:05:37.040 --> 00:05:44.359
is driven by moves. So, I hit the `m` key,
00:05:44.360 --> 00:05:46.879
and all the moves show up.
00:05:46.880 --> 00:05:49.479
Now, I don't think I need to espouse
00:05:49.480 --> 00:05:52.679
the virtues of completing-read enhancements like Ivy.
00:05:52.680 --> 00:05:55.559
Here, I'm using orderless with vertico
00:05:55.560 --> 00:05:57.719
to help me find my choices.
00:05:57.720 --> 00:06:03.639
Since I've discovered a site, let's play that move.
NOTE Reference
00:06:03.640 --> 00:06:06.479
I seldom remember the details for the moves,
00:06:06.480 --> 00:06:09.159
so I figured, why not put the text of the book
00:06:09.160 --> 00:06:11.799
in an Org file and show it in a side window?
00:06:11.800 --> 00:06:15.439
The prompt at the bottom, asking for a name,
00:06:15.440 --> 00:06:18.199
is driven by the content in the displayed Org file.
00:06:18.200 --> 00:06:21.119
This allows me to enhance my game without
00:06:21.120 --> 00:06:25.159
changing the original code. So, let's call this story arc,
00:06:25.160 --> 00:06:31.839
Exploring the Catacombs of Svala's Blood.
00:06:31.840 --> 00:06:34.679
Ooh, sounds epic.
NOTE Story arcs
00:06:34.680 --> 00:06:37.239
Ironsworn tracks the beats of a narrative,
00:06:37.240 --> 00:06:40.799
so major plot points take up more room in the fiction
00:06:40.800 --> 00:06:42.759
than minor plot points.
00:06:42.760 --> 00:06:45.039
Similar games like Blades in the Dark
00:06:45.040 --> 00:06:48.199
use numbers to track these, so you can say something like,
00:06:48.200 --> 00:06:51.079
we're three quarters of the way through this story arc.
00:06:51.080 --> 00:06:53.119
Ironsworn just uses labels,
00:06:53.120 --> 00:06:55.839
and while I want this particular story arc
00:06:55.840 --> 00:06:59.519
to be significant, I really just want to get in,
00:06:59.520 --> 00:07:00.959
find this person, and get out.
00:07:00.960 --> 00:07:04.039
So, I'm going to call this "short".
00:07:04.040 --> 00:07:09.279
Next, it's asking about an Org mode header placement.
00:07:09.280 --> 00:07:12.199
While I originally wanted my Org files
00:07:12.200 --> 00:07:13.799
to be completely flexible,
00:07:13.800 --> 00:07:15.919
one thing I noticed in playing
00:07:15.920 --> 00:07:17.999
is that a pattern always emerged.
00:07:18.000 --> 00:07:22.639
The story became a tree. You see, story arcs
00:07:22.640 --> 00:07:25.559
were just a series of montages or scenes,
00:07:25.560 --> 00:07:27.919
and each of those were made of a series of events
00:07:27.920 --> 00:07:29.119
and challenges to overcome.
00:07:29.120 --> 00:07:32.799
So, each Org mode header has a track,
00:07:32.800 --> 00:07:35.719
which often becomes the number of subheadings.
00:07:35.720 --> 00:07:40.639
At any point, I can see how much track is being made.
00:07:40.640 --> 00:07:47.239
So, for instance, this one seems to be
00:07:47.240 --> 00:07:48.679
about a third of the way through.
NOTE Using different stats
00:07:48.680 --> 00:07:52.599
So, let's dive into this ancient place.
00:07:52.600 --> 00:07:55.719
Since I've been walking through a misty forest,
00:07:55.720 --> 00:07:59.319
I can imagine vines hiding an immense door
00:07:59.320 --> 00:08:01.959
and a humid, earthy smell as I peer inside.
00:08:01.960 --> 00:08:04.319
But I don't have to write that stuff down,
00:08:04.320 --> 00:08:06.919
or if I want to practice my writing, I can.
00:08:06.920 --> 00:08:09.359
I can imagine the place is dark,
00:08:09.360 --> 00:08:10.839
so Tegan lights a torch
00:08:10.840 --> 00:08:13.039
before peering into this obscure world.
00:08:13.040 --> 00:08:15.799
As this move mentions,
00:08:15.800 --> 00:08:20.279
the next move to make is called Delve the Depths.
00:08:20.280 --> 00:08:26.159
As soon as I select this move,
00:08:26.160 --> 00:08:31.319
it shows up on the side window, and explains that,
00:08:31.320 --> 00:08:34.399
depending on how you're moving through
00:08:34.400 --> 00:08:36.239
this ancient catacombs,
00:08:36.240 --> 00:08:38.759
is what kind of stat I roll against,
00:08:38.760 --> 00:08:41.039
and those stats show up at the bottom.
00:08:41.040 --> 00:08:45.479
You know, if I'm sneaking around, you roll against "shadow".
00:08:45.480 --> 00:08:47.719
If you're trying to go as fast as you can, it's "edge".
00:08:47.720 --> 00:08:51.679
But I kind of imagine that he's thinking through,
00:08:51.680 --> 00:08:53.679
being very careful about it.
00:08:53.680 --> 00:08:55.759
So, I'm going to select "wits".
00:08:55.760 --> 00:08:57.719
And I don't have any modifiers.
00:08:57.720 --> 00:08:59.559
Just about every one of my stats prompts me
00:08:59.560 --> 00:09:02.959
if I want to add or subtract any values.
NOTE Dice rolls
00:09:02.960 --> 00:09:09.879
A miss. I should explain how the dice roll in this game.
00:09:09.880 --> 00:09:13.399
The downside to Ironsworn is that
00:09:13.400 --> 00:09:16.839
the dice mechanics are more cumbersome than other games.
00:09:16.840 --> 00:09:20.199
You roll a 6-sided die, add to it your relevant stat,
00:09:20.200 --> 00:09:24.599
plus any modifiers. Next, you roll two 10-sided die
00:09:24.600 --> 00:09:25.799
and see how it compares.
00:09:25.800 --> 00:09:28.679
Of course, I programmed this in Lisp,
00:09:28.680 --> 00:09:31.599
but when I displayed it, I wanted to see all the dice.
00:09:31.600 --> 00:09:34.799
And I also just wanted to see the end results.
NOTE Dangers
00:09:34.800 --> 00:09:37.479
So I colored it. I rolled a miss,
00:09:37.480 --> 00:09:39.799
which means I need to reveal a danger.
00:09:39.800 --> 00:09:43.519
Sure, I could imagine all sorts of dangers,
00:09:43.520 --> 00:09:44.359
but this is a game.
00:09:44.360 --> 00:09:48.359
I've already made a random generator for dangers.
00:09:48.360 --> 00:09:51.719
In fact, I've made a random generator
00:09:51.720 --> 00:09:55.479
for dangers in an ancient underkeep.
00:09:55.480 --> 00:10:00.879
Discovery undermines or complicates the quest.
00:10:00.880 --> 00:10:09.719
Hmm, a complication for finding the chief's son?
00:10:09.720 --> 00:10:13.319
What about a labyrinth full of hallways and levels
00:10:13.320 --> 00:10:16.599
with lots of choices and almost no way of finding them?
00:10:16.600 --> 00:10:19.679
Yeah, that sounds like it fits pretty well.
NOTE A strong success
00:10:19.680 --> 00:10:26.959
Time for another move. This time, we're going to
00:10:26.960 --> 00:10:28.799
gather information,
00:10:28.800 --> 00:10:32.279
see if we can figure out which way to go.
00:10:32.280 --> 00:10:34.719
A strong hit. Excellent.
00:10:34.720 --> 00:10:38.399
I imagine Tegan noticing footprints in the dust
00:10:38.400 --> 00:10:40.439
and knowing where to go.
00:10:40.440 --> 00:10:44.319
The game suggests that when you get a strong success,
00:10:44.320 --> 00:10:45.799
you can increase your momentum.
00:10:45.800 --> 00:10:48.879
These game mechanics
00:10:48.880 --> 00:10:51.754
come into play later, but this function here
00:10:51.755 --> 00:10:57.880
allows me to adjust that stat +2.
00:10:57.881 --> 00:11:01.460
I don't even have to scroll to the top of the buffer
00:11:01.461 --> 00:11:04.820
and edit that value in my properties.
00:11:04.821 --> 00:11:08.159
At any point, I can take a look at those stats
00:11:08.160 --> 00:11:10.439
and see how they measure up.
00:11:10.440 --> 00:11:13.159
Again, I don't have to scroll up
00:11:13.160 --> 00:11:14.879
and take a look at my properties
00:11:14.880 --> 00:11:16.559
at the top of the Org mode file.
00:11:16.560 --> 00:11:19.239
That's how I play the game.
00:11:19.240 --> 00:11:24.639
It's just a recursive loop of playing a move,
00:11:24.640 --> 00:11:27.319
rolling some dice to see how it works,
00:11:27.320 --> 00:11:30.159
trying to answer the question
00:11:30.160 --> 00:11:33.679
based on your own imagination or random tables,
00:11:33.680 --> 00:11:35.599
which the game calls oracles,
00:11:35.600 --> 00:11:41.199
and play creatively until you decide to take a break
00:11:41.200 --> 00:11:42.399
and pick it up another time.
00:11:42.400 --> 00:11:46.999
I think you get the gist of how I play
00:11:47.000 --> 00:11:49.679
this dice and pencil game in Org Mode.
NOTE Other solo RPGs
00:11:49.680 --> 00:11:54.039
However, I found more solo RPGs to play.
00:11:54.040 --> 00:11:57.319
And of course, I want to render them in Emacs too.
00:11:57.320 --> 00:12:00.799
This code for Ironsworn was a bit too specific,
00:12:00.800 --> 00:12:04.759
so I decided to create a role-playing game toolkit.
00:12:04.760 --> 00:12:09.599
This project is still in the early stages,
00:12:09.600 --> 00:12:12.199
but I've created some functions
00:12:12.200 --> 00:12:16.719
for mimicking rolling dice, including a mini-DSL for
00:12:16.720 --> 00:12:19.799
making dice mechanics
00:12:19.800 --> 00:12:22.839
typical of many role-playing game systems.
00:12:22.840 --> 00:12:26.519
I've also ported over the random table system.
00:12:26.520 --> 00:12:30.479
A text file can just list entries to be displayed at random.
00:12:30.480 --> 00:12:33.959
I love that I can put dice expression
00:12:33.960 --> 00:12:35.799
and word choices in the entries.
00:12:35.800 --> 00:12:39.439
One type of random table allows you
00:12:39.440 --> 00:12:41.559
to essentially copy and paste a table
00:12:41.560 --> 00:12:43.799
from a published game into a text file.
00:12:43.800 --> 00:12:47.879
A frequency table is what I'm calling
00:12:47.880 --> 00:12:50.879
a list of random entries where some entries show up
00:12:50.880 --> 00:12:55.959
more often than others. I'm working on generalizing
00:12:55.960 --> 00:12:59.959
the character sheet attributes as Org properties,
00:12:59.960 --> 00:13:04.719
so if you're interested, check out the project at Codeberg.
NOTE Conclusion
00:13:04.720 --> 00:13:10.359
The point of my presentation is not to show off Ironsworn,
00:13:10.360 --> 00:13:14.079
how I programmed it, or even this new toolkit.
00:13:14.080 --> 00:13:17.559
You see, most engineers,
00:13:17.560 --> 00:13:20.479
when they get an idea for a game like mine,
00:13:20.480 --> 00:13:24.079
would make a web app. Nothing wrong with it.
00:13:24.080 --> 00:13:25.959
More people can play it,
00:13:25.960 --> 00:13:28.199
but web apps suffer from text entry.
00:13:28.200 --> 00:13:30.959
And don't tell me you prefer the keyboard interface
00:13:30.960 --> 00:13:35.959
to Google Docs. Oh, and the JavaScript framework du jour?
00:13:35.960 --> 00:13:40.399
Oh, I mean, that's a huge barrier of entry
00:13:40.400 --> 00:13:42.039
when all you want to do
00:13:42.040 --> 00:13:44.359
is have a bit of fun prototyping a game.
00:13:44.360 --> 00:13:48.479
What I'd like to impress upon you
00:13:48.480 --> 00:13:53.999
is that hacking Emacs to make personal games is a trip.
00:13:54.000 --> 00:13:57.359
Learning Lisp is, it's easy.
00:13:57.360 --> 00:14:00.919
And more, Emacs Lisp has some, well sure,
00:14:00.920 --> 00:14:04.519
it has some cruft. But really, some of those features
00:14:04.520 --> 00:14:07.599
that I would hate at a distributed system at work,
00:14:07.600 --> 00:14:10.919
like global variables, makes hacking easier
00:14:10.920 --> 00:14:14.719
when you just want to have some fun in your own system.
00:14:14.720 --> 00:14:19.599
So, grab your laptop, sink into your comfy chair,
00:14:19.600 --> 00:14:21.599
pour yourself a glass of scotch,
00:14:21.600 --> 00:14:24.719
and craft yourself an enjoyable evening.
00:14:24.720 --> 00:14:35.920
Happy hacking, my friends.