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.