Any sufficiently advanced hobby is indistinguishable from work.
The epigraph of unknown origin invokes one of the three famous laws of the future. Below is the original:
Any sufficiently advanced technology is indistinguishable from magic.
On Sunday mornings, I’ve been facilitating a Stars without Number campaign. You can read about it in my Campaign: New Vistas in the Thel Sector series.
While I prefer in-person gaming, I’m coming around to having access to my work tools while running a game.
Tools of my Hobby
Earlier, I wrote
gm-notepad and a
gm-notepad data-set for Stars without Number. Truth be told, I used the tool once while running a face to face game. It was alright. The tool worked at the command-line level which hindered its utility.
At the time, Alex Schroeder said he would just use Emacs for such a thing. I wasn’t using emacs in , so that path felt prohibitive; And I certainly wasn’t going to make a full-blow application out of
I don’t regret the time I spent building the tool. I learned more about low-level tools, tokenization, refactoring, and ePubs.
, I use the following tools for the Stars without Number game:
- Online Video
- Either Zoom or Google Meet; I really just need audio.
- A highly configurable text-editor.
- Org Mode
- An organizational framework built into Emacs.
- Org Roam
- An organizational module that brings aspects of Zettelkasten.
- Command line tools for generating random Stars Without Number results.
- Custom Emacs Functions
- The tools that I wrote.
Let’s dive into each of those components.
I’ve been writing code for at least 30 years, however, this is my first year of elisp 📖 and Emacs. Several years ago, I poked around Lisp and Clojure, but I didn’t have a compelling reason to learn them.
When running a game on-line, I don’t use a virtual table-top nor a dice roller. Instead we go for a Theater of the Mind approach. I describe the situation and relative positioning (if applicable). The players ask for clarification, and we talk it out. Players also roll physical dice and tell me the results.
I find the virtual table tops to interfere with the kind of play I’m looking for. I don’t want to focus on moving pieces on a board; I reserve that for some of my face to face play. Instead I want to focus on the fiction we’re producing. And yes, a map can help facilitate that, however spending t
This is my text editor of choice. I use it for writing software, blogging, taking meeting notes, and just about anything text based.
Over on Reddit, someone asked What’s your job? What’s your daily emacs workflow?
The responses surprised me. Software developers and scientists use Emacs. But so do lawyers, nurses, and authors. It makes sense, Emacs is a great tool for consolidating your text-based work.
When talking about why I chose Emacs, I wrote a short section about Org Mode.
For the purposes of my campaign notes, I don’t use much of the Calendar and Agenda feature. Instead I use the
org-mode syntax and publishing features.
org-mode syntax is similar to Markdown, but it’s the tools built on top of
org-mode that make it shine.
First, I learned about Comment Lines. I write Referee notes as comments. These comments won’t make their way to my blog and public session reports.
Second, I use
org-export-dispatch to export the org file to Markdown.
My blogging engine, Hugo 📖
org as a valid extension, but for historical reasons everything I write for the blog is in Markdown. I may make the switch, but for now am keeping that separation.
The export skips over comments, and leaves me a mostly ready to publish markdown file.
Third, I can use the
+INCLUDE directive. Per the Include Files documentation, I can use
+INCLUDE to aggregate multiple files into a single file export.
For example, I could create an
org file for a single encounter that I plan. In that encounter, I might have Referee notes, Read Aloud text, Design Notes, as well as play throughs of that encounter. If I wanted to generate a “publishable” adventure, I could use
+INCLUDE to target just the Notes and Read Aloud text. It’s the versatility of composition that I love.
I also wrote a section about Org Roam. For my campaign, I make extensive use of
org-roam provides tooling to build a campaign wiki.
While I’m taking notes, either before, during, or after a session, I can invoke
org-roam-insert (via CMD+i) to quickly find or create a new note. With that note
org-roam creates a link at the point of my cursor.
org-roam also queries for backlinks. Let’s say I’m looking at the note for “Vern Schultz”, a player character in the game. I can see all notes that have a link to Vern Schultz. That’s super handy.
It also ships
org-roam-server, which provides a graphical view of the network graph of notes.
SWNT is a command line tool written in
go-lang. It provides a means of generating results from the random tables in the free version of Stars without Number.
swnt to generate the initial Thel Sector. It’s output is text-based and by extension highly portable. I’ve moved it around, repurposed it, and ran scripts against the initial sector output.
While running a Stars without Number game, I keep a terminal open for
swnt. Most often, I use it to generate the name of an NPC 📖
Custom Emacs Functions
swn-npc, an interactive Emacs function that pipes the
swnt new npc output into a file and adds a link to that file at the point of my cursor.
This behaves similar to
org-roam-insert, except instead of attempting to find an NPC
, it just makes one.
In other words, when I need a random character, I run this function to get a random person’s name that links to their details. I may or may not use those details, but they are there for reference.
See the Emacs Code
You can also see this code over on Github
(defun thel-sector-filename (title) "Convert the given TITLE to a filename. The filename is conformant to my org-roam capture templates." (f-join org-directory "projects" "thel-sector" (concat (format-time-string "%Y%m%d---") (s-snake-case title) ".org"))) ;; TODO - Can this be shifted to an org-roam capture template? (defun swn-npc (culture &optional) "Capture a random `swnt' npc of the prompted CULTURE. This function writes to the as-of-now hard-coded Thel Sector project in my org-directory." ;; Prompt for a culture that will be used as the basis for the ;; random name. (interactive (list (completing-read "Culture: " '( ("Arabic" 1) ("Chinese" 2) ("English" 3) ("Greek" 4) ("Indian" 5) ("Japanese" 6) ("Latin" 7) ("Nigerian" 8) ("Russian" 9) ("Spanish" 10))))) (let* ( ;; Get a list of the lines output from ;; swnt's command. (npc-string-list (split-string-and-unquote (shell-command-to-string (concat "swnt new npc " "--is-patron " "--culture " culture)) "\n")) ;; Extract the NPCs name (npc-name (string-trim (replace-regexp-in-string ":" "" (car npc-string-list)))) ;; Build the document's body, conforming ;; to org-mode format. (body (concat "#+title: " npc-name "#+roam_tags: npc" "\n\n* " npc-name "\n\n" (string-join npc-string-list "\n"))) ;; Get the path to the file (fpath (thel-sector-filename npc-name))) ;; Write the body to the file at the FPATH. (write-region body nil fpath nil nil nil t) ;; Insert at point an org-mode link to ;; the randomly generated NPC. (insert (concat "[[file:" fpath "][" npc-name "]]")) ;; Rebuild the org-roam cache, as I've just added an NPC. This is ;; a kludge, as I'm treating org-roam as a black box. My ;; preference would be to avoid rebuilding the cache, but instead ;; rely on the inner workings of org-roam to do this work. If I ;; go the path of an org-roam capture template, that would work. (org-roam-db-build-cache))) ;; Given that I'm in an org-mode context, then the following kbd ;; combination will prompt to generate a random SWN npc. (define-key org-mode-map (kbd "C-c s n") 'swn-npc)
A Random NPC from
Below is an
org-mode file, built from the
swnt output and the
#+title: Dmitri Sikorski #+roam_tags: npc * Dmitri Sikorski Dmitri Sikorski : Culture : Russian Gender : Other Age : Middle-aged or elderly Background : The local underclass or poorest natives Role in Society : Military, soldier, enforcer, law officer Biggest Problem : Drug or behavioral addiction Greatest Desire : They want fame and glory Most Obvious Character Trait : Devotion to a cause Hooks : Initial Manner : Somewhat intoxicated by recent indulgence Default Deal Outcome : They firmly intend to actively betray the PCs Motivation : Greed for wealth and indulgent riches Want : Retrieve a lost or stolen object Power : They control the use of large amounts of violence Hook : Visible signs of drug use Reaction Roll Results : Positive, potentially cooperative with PCs Patron : Patron Eagerness to Hire : Desperate, might offer what they can’t pay Patron Trustworthiness : They’ll pay without quibbling Basic Challenge of the Job : Get proof of some misdeed Main Countervailing Force : The job is spectacularly illegal Potential Non-Cash Rewards : Information the PCs need Complication to the Job : Critical gear will fail partway through
An Example Walkthrough of My Tool Chain
Before a session, I go to my “New Vistas in the Thel Sector Sessions” document; It lists all of the session recaps.
To create a new session document, I use
org-roam-insert (via CMD+i) to prompt me to either find or create a document.
I type “New Vistas in the Thel Sector Session NN"
where NN is the next session number
and hit enter.
org-roam didn’t find this file so it creates one.
Because I’m creating a new document, I need to pick a template. I choose the “Project > Thel Sector”.
I have one template per project. The template defines where I’ll file the document.
I now have a blank document. I start filling out the boilerplate information for a sesson. As a todo, I should probably create a snippet that is a template for my sessions. For now, I usually copy from a previous session.
I save the document, and Emacs writes a link to the new document to the existing “New Vistas in the Thel Sector Sessions” document.
As part of my preparation, I review past sessions, maybe creating new documents or connecting to existing documents. For the upcoming session, I start writing notes. Some notes are only intended for me (e.g. Notes about the off-camera actions of any opposition). Those Referee only notes I mark as comments. Other notes might be “Read Aloud” text or bookkeeping notes.
I might also launch
org-roam-server; This creates a web page at http://localhost:8080/ that I can use to explore the links between documents. It’s a quick way to sift through the documents.
During the session, I keep “New Vistas in the Thel Sector Session NN” open. I write down my notes. I’ll use
org-roam-insert to connect to documents that come up.
For example, I might write the note “The character’s head over to the Grand Kall Theatre to talk with Suliat Adunola. The bold words are links to corresponding documents.
If I need an NPC, I invoke
swn-npc (via Ctrl + c s n), and poof, I get a random PC name along with results from all of the NPC random tables. I might ignore those results or dive into those for further inspiration.
As I sift through my NPC notes, I remove any unused or unreferenced aspects of these random NPCs.
Once the sessions over, I go back to my session notes and start converting those notes to a blog post. If I find something interesting that I don’t want in my blog, I’ll mark it as a comment. I’ll also read through and look for any “notable” things that might merit their own note. I’ll create those.
Finally, I export the “New Vistas in the Thel Sector Session NN” and do a quick conversion to get it ready for my blog.
Much of my day is spent writing. It might be software, blog posts, meeting minutes, documentation, or session notes. I’m striving to continue to use the same toolset for all of those. That way, when I learn something new during one task, I might be able to bring it to another task.
In writing this blog post, I started thinking about different approaches I could try. I’ll just keep folding my processes back into my tool chain and see what comes out next.
I’ve been writing this blog post as my computer cycles through OS 📖 upgrades. During that time, on another device, I looked through the schedule for the upcoming Emacs Conference 2020. The talks cover a wide-spectrum of how people use Emacs. I’m looking forward to the free 2-day conference on and . I’m sure I’ll pick up more ideas for extending Emacs to best suit my needs.