Friday, June 14, 2024

The Endless Storm of Dagger Mountain: A short adventure game that is Powered by the Apocalypse

Introduction and background

Last night, I released a new game into the wild: The Endless Storm of Dagger Mountain. I submitted it to Crossroads Jam 2024, a statewide game jam sponsored by the Indiana Gamedevs community. 

This game scratches a creative itch that I've had for over two years: what happens when you apply Apocalypse World style rules in a digital game? I've had this as a component of a few different design explorations, none of which bore any fruit—sometimes because they weren't fun and sometimes because their scope exploded. In fact, in May, I started work on a project that was growing too large, and it included PbtA elements. By the last week of May, I had put that side project to rest. I decided that I could use Crossroads Jam as an excuse to isolate just this single design idea—digital PbtA—and package it up into a jam-sized game. Readers may be interested in looking at my previous exploration of tabletop PbtA, which took the form of Kapow! The Campy Superhero Role-Playing Game. I also wrote an essay comparing the math of PbtA and d20 systems.

I was a little disappointed that the theme "severe weather" won the polling on the Indy Gamedevs Discord. Since this is the first Crossroads Jam, it seemed like a great opportunity to highlight something positive about the state. You know, like corn. More seriously, there are a lot of great things about Indiana, including globally-recognized events like the Indianapolis 500. And corn. But I digress, and others preferred "severe weather." I had been wanting to explore some pulp fantasy writing a la Robert E. Howard's Conan stories, which I read a few years ago. This presented a good opportunity: a lone, stoic hero, making a long journey up to the top of a mountain where dark magic has brought about the destruction of the innocent.

Game and Narrative Design

Most of the writing is really just a first draft. Despite years of gamedev, I have done barely any game writing. It felt good to get my hands dirty and create enough content to carry the gameplay. I estimate I spent about 15 hours just writing content for a game that takes a few minutes to play. The writing was enjoyable, but especially as I got tired, I couldn't shake the feeling that much of the text was low stakes. Each dice check has at least three paths—succeed, succeed at a cost, and fail—and I tried to keep them equally interesting. I do not spend a lot of time with interactive fiction, but I quickly ran right into the same problem that any narrative designer has: with chance or with agency comes the loss of authorial control. There are a few specific scenes in the game that I would like to spend more time on, to make the story more compelling and to evoke Howard more strongly.

I was not able to pull in all of the tabletop inspiration that I originally wanted. In tabletop games, I love the idea of the Countdown Clock or what Runehammer calls Timers. A simple timer that ticks down becomes a source of tension for the player and, in the system, it's another formal element to manipulate. In my first draft of Dagger Mountain, I had a timer that, if it ran out, would cause the game to end before the player could summit the mountain. This worked well as a penalty, especially when asking the player to choose between attribute reduction and advancing the timer. However, the game ended up being too short to make the timer meaningful. In fact, one of the problems that inspired me to think about removing the timer was the trouble of visually representing a "timer" as something with only two or three units. I could not reasonably balance it and give it a significant value: a timer with two clicks feels more like it's depleting some other resource rather than feeling like time.

The other common PbtA element that I didn't add was what Apocalypse World calls "reading a sitch." The idea here is that a player can spend an action trying to understand a situation, where success or partial success determines how many questions they can ask about it. Systematically, this is simple: I could have a preconstructed list of questions and answers, and these could provide lore and setting information. As I got into building the narrative, this felt like it would not have a good return on investment: every other decision produced changes in the world state, such as modifications to attributes, and I didn't want "reading the sitch" to be a wellspring of mechanical benefits. It would be relatively easy to add this into my software since it was in mind from the beginning, but it did not find its way into this project.

Speaking of attributes, I still have something of a pipe dream that one could make an RPG attribute system that is consistent with Thomistic philosophy. Consider that the legacy of Dungeons & Dragons presents a sort of dualism, that the mind and the body are separate. Yet, as confirmed by a recent conversation with a weight-lifting friend of mine, the two must work together: it's not clear that Strength (as physical might) and Wisdom (as willpower) are independent variables, for example. I couldn't find a way to distill a more Aristotelian view of the human person into three or four attributes, but reviewing Apocalypse World's attributes, I was reminded that they describe how one does something rather than what someone is. That's a great hook for future design work. In the meantime, for this project, I made a list of actions that I wanted the player to perform, knowing the genre and setting, and I categorized these into the three attributes that are in Dagger Mountain: bold, determined, and savvy. I admit that there are a few stretches in the game where penalties might be hard to classify in these ways, but I will be curious to hear what players think about them as a trio.

Technical Considerations

Inspired by Knights of San Francisco and the beauty of Dart, I started writing Dagger Mountain in Flutter. It's a beautiful way to write applications, but there's a significant difference between declarative and imperative UI programming, and I find that I stutter a bit when I hop between them. I set up the essential architecture and was enjoying myself until I tried to implement some UI features, specifically the scrolling list of text and buttons along with placeholder animations. There is a lot of typing required, animations were hard to debug, and it wasn't always obvious where my problems came from. I am sure there was a lot to learn from the endeavor, but I was on a deadline and wanted to get things up and running quickly. In transitioning back to the comfort of Godot Engine, I realized something: while dart's asynchronous programming features are wonderfully expressive, there is a real power in GDScript's simple signal syntax. It is hard to get more terse than that, although it comes at the cost of not having explicit control over things like Futures. I returned to Godot Engine, re-creating everything I had written in dart in very little time. With the design decisions made, I just had to interpret it in the new environment and type it up.

I spent too much time in Godot Engine adding dynamic font resizing. I knew I wanted the game to run comfortably on a desktop or mobile browser, and giving the player control of font size seemed the best way to do this. It required a lot of shenanigans with theme overrides, and as I added more visual elements such as the visible dice, it got more and more convoluted. Near the end of development, I just gutted this feature from the play experience and put a configuration on the main menu, which allowed me to just fiddle with the values in the main theme rather than deal with distributed theme overrides. What really irked me was when I started working on deployment, realizing that the cleanest solution for the player would be to use the browser's built-in font rendering and resizing... the way that a Flutter app would have done. Sigh.

Here are a few summary observations along these lines. Flutter is great for its static typing, robust asynchronous programming support, autoformatting, built-in browser font resizing support, spread operator, null safety, and most importantly, refactoring support. Godot Engine clearly wins on terseness of signals and tweens and the ability to rapidly build and test scenes independently of each other. To clarify that last point, I regularly decompose my Godot Engine programs into scenes that I can run and configure by themselves, confident then in how they will work when instantiated as part of a larger system. In Flutter, I wish I could easily say, "Spin up one of these widgets by itself and let me tinker with it," but I have not found anything that comes close to Godot Engine's rapid development support this way.

Incidentally, I did briefly consider other options than writing my own engine. I am intrigued particularly by ink, which I have never used. I was hesitant to jump into something with such a different syntax, although I am sure I could learn a lot from it, too. What killed the deal for me though was that it wasn't clear to me that I could easily plug in the PbtA aspects that I wanted. I discovered a Godot Engine integration, so perhaps I will investigate that later this summer. It wasn't until my family was testing the game that one of them mentioned Dialogic, which I haven't used since Godot 3.x. I haven't looked at it to see if it could have been modded for my purpose. However, writing for Dagger Mountain made me appreciate why narrative designers need better tools than just piles of scripts and a notebook sketch.

Dagger Mountain is my first released game that uses an event queue to isolate the game rules from the interface. I have tinkered with this pattern in several abandoned projects. Two summers ago, I spent a lot of time studying egamebook and its architecture, and I learned a lot from it even though that particular summer project was never released. 

My approach separates the software into three parts. The module is the content of the adventure itself, the story of Dagger Mountain. Each scene in Dagger Mountain is a GDScript file that is given a reference to an Adventure object. The next part is the rules engine, which is manifest in an Adventure object. The scene is given a reference to an Adventure object, and the module tells it to do things like show text, modify attributes, or present a series of choices to the player. Internally, the rules engine generates events to correspond to these interactions, posting them to the event queue. The final part is the presentation layer, which subscribes to the event queue. It dequeues events, processes them, and then notifies the rules engine when it is complete. The code is all free, so feel free to look at the prelude scene for an example of how this works.

I decided early in the project that I would repurpose GDScript as my narrative scripting language rather than create an independent data format that would be interpreted. The primary reason for this decision was the pressures of time: GDScript is already a scripting language, so using its support for functions and conditionals would be faster than writing my own. This is true, but I hadn't considered all of the costs at the time. The game runs through function calls in the module layer, which is nice for terseness but actually makes it hard to test in a modular fashion. I am sure that if I had used TDD, I would have had a more testable architecture. I would much rather have test coverage of the whole state space of the game; instead, I have to hope that my manual testing was adequate.

I did add integration tests near the end because of the need to await basically every call in the module layer. Missing a single instance will break the player experience. I wrote a test that reads through the module layer and looks for cases where await is missing. It took a little time get the test working, but it immediately found a case that I had missed, so that was worthwhile.

Conclusions

I enjoyed building The Endless Storm of Dagger Mountain, and I hope you enjoy playing it. I think I will go and tweak some of that text with this morning's remaining coffee. Despite its small scope and shortcomings, I feel good about having built it. Not only does it explore digital PbtA in a way that I've been imagining for a few years, it also gave me an opportunity to do some creative writing and build more empathy for narrative designers. 

Regarding digital PbtA, I think the jury is still out, since for every promise it has, it comes at the cost of dramatic increase in content creation costs. For interactive fiction, using PbtA resolution requires writing an enormous amount of text, much of which will never been seen my players. It is, of course, not the same feeling as the give and take of a tabletop RPG. However, I can see opportunities for using this resolution system if there were more supporting systems. In a larger game, for example, one could put back in "reading the sitch" style actions that give clues to puzzles. I prefer losing attribute points over the abstraction of hit points, particularly for a narrative-focused game, but I think this would benefit from more explicit representation. Gaining statuses like "confused" or "twisted ankle" would help carry the narrative forward, but then these would be most meaningful if worked into the other systems or stories of the game. All that being said, I appreciate how the PbtA elements feel more like a description of a whole human person than do hit points, armor classes, and the six classic attributes.

Thanks for reading. Let me know what you think of the game!

Wednesday, June 5, 2024

Summer course revisions 2024: Game Programming (CS315)

It's time again for Summer Course Revisions. I spent this week focused on my Game Programming course, which is a junior-level elective for most students and required for the Game Design & Development concentration. I think it is one of my best courses, and I feel good about the general stability of it.

In preparation for revisions, I went back to my notes from last year, including my reflective blog post from last Fall and my notes from reading Grading for Growth. I also referenced my internal notes, which I keep in my course planning spreadsheet. The most important things I came across here were: a reflection on the idea of using "more hurdles" instead of "higher hurdles" for specifications grading; and the need clean up how stars were earned on the final project to remove shortcuts. The latter is something I will have to consider later in Fall since, to address the former, I decided to make a dramatic change in the grading scheme.

This course is where I pioneered checklist-based grading, which I also wrote about in an academic paper. As my post from last Fall makes it clear, though, something shifted in my teaching experience that led to significant frustrations with that approach. I suspect the causes are cultural and not personal, but you have to negotiate with a system. I decided to try an alternative inspired by Joshua Bowman's work that is documented in Grading for Growth. In particular, I am replacing the higher-hurdles specs approach with atomic, clear goals and multi-tiered resubmission. The overall structure of the semester will be the same, and I expect the primary student activity to be exactly the same; the changes are almost entirely in the activity systems around assessment.

I have rewritten the first three weeks' assignments as a proof of concept. For each, I removed the checklists and replaced them by an articulation of essential and auxiliary goals. The essential goals are always required for a satisfactory grade, and when there are auxiliary ones, a subset of them are required. Each goal is graded on a four point scale. Successful has an obvious meaning. Needs minor revision is for the cases where it's mostly right, but something crucial needs to be addressed to show understanding. These minor revisions can be done within three days, accompanied by a short report explaining what went wrong. New attempt required is for cases where something critical is wrong; related to that is Incomplete, for work that is not done. These latter two require a more significant reworking by the student, and I've put in a throttle similar to what I use in CS222: one resubmission per week. 

Concomitant with this change is a revision to course grades. I have written before and done several experiments regarding the assigning of course grades. One of the things I really liked about my old approach to Game Programming in particular was that it was easy to give each week equal contribution to the final grade. However, exercises are now being evaluated as either satisfactory or unsatisfactory, and it's not clear that this categorical data makes sense "averaged" with other data. I have put together a simple specification table, akin to what I did last Fall in CS222.

I am hopeful that this approach will alleviate some of the frustration of students' mismanaging the checklist system. It narrows the number of things students need to think about at the cost that each item is slightly larger now. 

I have not written up policies for the midsemester exam and the final project yet, but my inkling is to pull out specific objectives in which each student must individually show competency. This would represent something much more like a transition from "higher hurdles" to "more hurdles," as long as I can make the hurdles roughly the same size. I am also considering dropping the team requirement from this course. Teamwork is common but not essential to game development. The students in the GDD concentration will have opportunities to work in teams in the production capstone sequence, where the students from other majors won't have teamwork experience anyway. I would rather my CS students' skills be all up to snuff coming into that sequence than that they've already been introduced to interdisciplinary teamwork concepts that will have to be in that sequence anyway.

The other major change for Game Programming is more technical. For years, I've maintained my own websites for my courses, and I've done that for three main reasons: it gives me complete control over the presentation and public nature of the content; it gives me reliability in case of the campus systems' going down; and I can use my software development knowledge to separate model and view. My system for representing, rendering, and downloading checklists was pretty robust, but its assumptions also weaved through the whole course site. When I started reconfiguring my template to handle these changes, I ran into common Web development frustrations: changing dependencies, confusing errors from libraries, and CSS. I decided to pivot and just put all the content onto GitHub. This is what I did with my games capstone and preproduction classes last year as an experiment. It's not ideal, but it meets several of my needs. 

You can find the current draft of my course plan at https://github.com/doctor-g/cs315Fa24 in case you want to take a look. As usual, the content is licensed under CC BY, so feel free to adapt it for your own teaching and learning purposes. I wanted to experiment with how Canvas might link to the individual exercises and their assessment goals, but Fall's schedule isn't loaded into Canvas yet, which points to yet another reason not to bind one's course planning to that platform.

Monday, June 3, 2024

Something like a latte

It's time for that recurring feature, "What is Paul drinking?"

The warm summer weather turned my mind toward coffee concentrate. I use a simple approach gleaned from online advice: fill a pitcher 24-35% with ground coffee, top it off with water, and let it sit on the counter for eight hours or so to brew, shaking occasionally. This gives a strong, dark liquid that is a great base for iced coffee, and I often have both decaf and regular in my fridge in season. It also works for a hot morning cup in cases where I am out of whole beans, which happened to be the case last week. 

The last week has been quite mild, the high temperatures only being in the low 70s. It wasn't the right weather for iced coffee, so I wondered what else I could do with my concentrate. I came across a site describing how to make a latte at home with no fancy equipment, and this inspired me to try something like that myself. Turns out, I can make something at home that is a lot like the cappucino I might order at the Bookmark Cafe.

Here's what I've been tinkering with:

  • About two ounces of coffee concentrate in a coffee cup, heated in the microwave for thirty seconds
  • Just under six ounces of whole milk in a ball jar, heated in the microwave for a minute
  • Froth up the milk using the fancy battery-powered handheld frother that, until recently, I didn't know we had in our kitchen utility drawer
  • Slowly pour the milk into the coffee cup, which will leave the foam on top.
The result is quite nice. It's not that much complicated than my usual French press coffee. It is pretty, and if your pants are really fancy, you can sprinkle cinnamon on top. The flavor and mouthfeel are pleasant, and I don't think I could tell you if it was made with espresso or coffee concentrate. I call this a successful experiment, and there's a good chance I'll be making a decaf one this afternoon. 

I normally don't sweeten my cappucino, but I wanted to try that last time I made one of these homemade lattes. I added some simple syrup and vanilla extract after combining everything, but I think if I did this again, I'd add it before frothing to eliminate stirring later.

I'm also working on a batch of mulberry mead, and I will have more to say about that later. In particular, I will probably say, "Don't put the bag of berries into the carboy because it will float to the top and cause headaches." 

An evening with Microscope

I heard about Microscope from Ben Robbins' interview on Justin Gary's podcast. It is a game about creating a history: the rules guide the players in the collaborative creation of the periods and events that make up a historical arc. I became intrigued, and it seemed like the kind of game one would have to play to follow a conversation about it. I borrowed a copy of the rulebook and convinced my wife and two elder sons to try it out with me.

The rulebook gives specific advice on how to introduce the game, and I appreciated being able to follow the script. Our first decision was the overall theme of the history, but we could not settle on one that we all liked. We agreed to take the first one of three that were recommended for players like us who weren't sure how to start: three nations are united as a single empire.

Our next step was to bookend history. One of my sons recommended that the end of the history should be that three nations, each on the back of a turtle, come together under one emperor. We then came up with the idea that at the beginning of history, there was one nation, on the back of the Great Mother Turtle, but she died, and the nation divided onto the backs of her three children.

As we got into the game, we created the history of three turtle-nations who lived in harmony until a blight caused scarcity of Turtle Orchids—the only food eaten by the giant turtles. The three turtle-nations separated to search for new sources, and their cultures evolved due to the different environments under which they found themselves. We never got into the details of how the turtle-nations came back together after this separation, especially not how they resolved religious differences that emerged, but we know they did, and that it was positive for them.

There were some rocky spots, as to be expected in any first play of an RPG where only one person has read the rules. The last page of the rulebook is a convenient rules summary, but Robbins has not provided this as a downloadable player aid. I feel like it would have helped the players—including me—keep some of the terminology and sequencing right. 

One scene did not go particularly well. Scenes answer particular questions in history, and this one was supposed to answer the question, "What monsters attacked Medium Turtle that caused the society to become more militaristic?" It was only our third played scene, and we jumped into it with gusto. It didn't seem to go anywhere, though, as no one roleplayed an answer that satisfied the question. At one point, I just put the kibosh on the scene, suggesting that the answer seemed to be that we didn't know. This was a little unsatisfying, but so was the scene at that point. 

I did some work afterward to better understand the rules for played scenes. The introductory advice that we followed had us start with a played scene, and that one had gone well. In re-reading that portion of the rulebook, though, I was reminded that playing the scene is a combination of narrative and dialogue. We had only been engaging in dialogue, and if I were to teach the game again, I would make sure to open a scene by using both. We also were too light with setting the stage, which is an explicit part of playing a scene: while we had established who and where we were, we had not established what we all knew and what happened prior. 

I came across two interesting resources during my post-play research. One is this rules cheat sheet created by Nicole van der Hoeven. It may be a good way to introduce someone to the game, but it's a great summary of the rules. Reading it provided a more convenient reminder about the core rules over re-reading the book itself, since the book necessarily combines rules and exposition. Seeing the topic list on van der Hoeven's site, I think I may spend some more time exploring her notes on other topics as well.

The other interesting resource I found was a recent blog post by Robbins himself. It presents alternate rules for scenes which I am sure would have given us a better experience even in our first play. Among the benefits of the revision is that it eliminates the need for "push" rules. These are the rules that allow players, during a played scene, to push back on something that someone has introduced into the world. They seemed necessary but secondary in the book, containing more details than I could hold in memory when teaching the game. They were to be deployed in reaction to play, which also meant that I did not want to review them while we were actually in the game. I am not just happy with the simplification of the scene rules, but I am also chuffed to see a designer improving a game he published over ten years ago.

In summary, I enjoyed my first play of Microscope, and I would like to play again, now having a better understanding of the rules and a handy revision thereof.  If I were teaching my game design class in Fall, I would likely bring this in as an example of an RPG. In an era where all of my students are at least aware of Dungeons & Dragons, it would be a great example of how "role-playing game" is bigger than that.

Monday, May 13, 2024

Letter to Sphere Province Games on the occasion of the launch of Mission Rovee

I shared a personal reflection about my work with Sphere Province Games at the launch party for Mission Rovee. At my college's request, I rewrote my comments in the form of an open letter. They have just published it as a featured blog from the College of Sciences and Humanities. You can read it here.

Thursday, April 25, 2024

Dante Alighieri on Social Media

In Canto 30 of The Divine Comedy, Dante lingers in the eighth circle of Hell, watching the damned insult and attack each other. Virgil, as the voice of reason and wisdom, calls him out on this foolishness. Dante repents, and Virgil responds,

"Never forget that I am always by you
should it occur again, as we walk on,
that we find ourselves where others of this crew
fall to such petty wrangling and upbraiding.
The wish to hear such baseness is degrading."

(John Ciardi translation)

Thursday, April 11, 2024

Using C# with Rider in Godot Engine on Ubuntu

I was inspired by the announcement of Slay the Spire 2 and Casey Yano's description of his experience with Godot Engine to investigate the C# bindings in Godot Engine. I've been using Godot Engine for years but only scripted it with GDScript. Like Yano, I prefer statically typed languages over dynamic ones, so this seemed worth a shot. I was introduced to Rider when I was doing C++ in Unreal Engine, and I found it to be an amazing IDE. This, combined with the availability of free academic licenses for people like me, made that my first stop for trying Godot's C# side.

Unfortunately, some of official documentation had me going in unproductive directions. That's why I am taking a moment here to share my quick notes about the experience. The most important thing I learned was not to bother with Mono: it is being phased out. If I had to do it all again from scratch, I would do something like the following.

  • Install dotnet SDK using Microsoft's feed. I used version 8.0 and that seemed fine.
  • Download and install JetBrains Rider. I did this with snap, which is how I've installed Android Studio for my Flutter work.
  • The first time you run Rider, go to the settings and install the "Godot Support" plug-in.
  • Of course, make sure you have the .NET version of Godot Engine, and tell Godot Engine to use Rider as its external "dotnet" editor.
That's it. Make a Godot Engine project, make some scene, and set it as the main scene to run. Then open the project in Rider, and everything else just worked. 

This was my trial case. At the time of this writing, it is the entirety of the C# code I have written for Godot Engine.


 using Godot;  
 namespace RiderTest;  
 public partial class World : Node2D  
 {  
   public override async void _Ready()  
   {  
     base._Ready();  
     await ToSignal(GetTree().CreateTimer(2.0), Timer.SignalName.Timeout);  
     GD.Print("Did it!");  
   }  
 }