Mailbag: High-Agency Narrative Systems

Today’s mailbag entry (at the request of the submitter, anonymized and edited a little) gets into the question of how to create salience and quality-based narratives and other similar games, given that typically one has to build one’s own.

What I keep getting drawn toward on a personal level is your work–and other people’s work too–on procedural narrative generation. I have enough knowledge of coding to understand states and modeling systems and when to move from one to another on a conceptual level, but at this point I could not make one in any language or engine. I think this is something I would like to learn to do more of...

Obviously I’ll need to learn a language from the ground up. That’s fine. I suppose I’m asking what would be most helpful to focus on–not just in terms of C# or Ruby or Python–but other skills as well… most of my questions/interests are about event generation, procedural chains of causality, etc.

Further discussion with the asker indicated they are talking about the kind of quality-based and salience-based narrative systems I wrote about in the article Beyond Branching; this RockPaperShotgun column about Alcyone also gets into some detail about the state of play in this space.

Systems like this can achieve a combination of player freedom and agency that is hard to reach in CYOA or any other node-based system (I would include ChoiceScript here): there are often dozens of viable choices available.

Meanwhile, because you’re not tied to a specific simulation concept (like the standard parser IF world model), you can adjust the qualities of your QBN to the particular needs of this work. Track your protagonist’s health, her interest in opera, her sense of humor, her tolerance for pain; track her relationships with each of a dozen friends, or a dozen aspects of her relationship with one friend.

On the other hand, the tooling and the design abstractions in this space are not nearly as advanced as they are for parser IF or CYOA/hypertext/stats-based IF, so if you want to work with it, you probably have to build your own.

That’s a topic that could take a number of articles, and there isn’t as much writing in this space to point to as in the parser space.

Building a QBN system. At the most basic level, implementing your own quality-based narrative system is not that hard. The basic unit of data is the storylet, which needs to contain, at a minimum,

  1. content (the text or image or whatever else is displayed to the player when they experience this story)
  2. prerequisites (usually a list of quality ranges that the player’s qualities have to fall into in order to unlock the storylet; in Fallen London, this will be stuff like Unaccountably Peckish 3 or Nevercold Brass 2400)
  3. results (usually a list of quality changes after the player experiences the storylet, which will in turn unlock new storylets)

This could be rewritten as “the world state determines what the player is currently allowed to do; everything the player does then updates the world state again.”

These concepts recur in some form in almost every form of IF: compare ChoiceScript’s *if (controlling access to text) and *set (changing stats again); or Inform’s Check/Carry Out/Report rules, which determine whether an action is permitted and then describe what happens in consequence. Even basic CYOA can be described this way if you think of the prerequisite as “the page number you are currently on” and the result as “the page number you go to afterward.”

Of course, StoryNexus and Fallen London offer a few extra features on top of this, among which are

  1. randomized success/failure where the storylet rolls the dice based on the player’s stats in some area when the storylet is selected, and then has two or more possible result outcomes depending on what the player chose. (This completes the Check/Set/Gate trio.)
  2. mandatory redirection where one storylet can send you on immediately to a second one without the option to do anything else
  3. locations as an organizing principle — conceptually perhaps just another kind of quality, but it makes the author’s life easier
  4. opportunity decks, where the player is presented with a limited set of storylets from a big collection, rather than being shown all the possible storylets at once. The opportunity deck is an important addition that opens up additional design space, because it permits narrative deck-building, where the player has some second-order intentionality around trying to improve the selection of storylets to which she has access, and her likelihood of encountering good rather than bad storylets. (This really deserves its own article.)
  5. the Bazaar, where you can effectively (by buying and selling) swap some kinds of quality for some others. StoryNexus games other than Fallen London don’t have the Bazaar because it is presumably a pain to implement and specify, but it was in the early days one of the key features in making the game fun and sticky to play. (This probably also deserves its own article.)

To implement basic QBN, you need to come up with your data structure — a chunk of text, plus how you’re going to express prerequisites and results.

If you want to get fancy, you might allow a storylet to have internal structure as well. Varytale basically embedded Twine-like passages inside its storylets. Or you might let the content text do some procedural text generation, as Voyageur does, allowing your contextual stories to do even more to callback to the precise details of world state. Some time back, Failbetter introduced to StoryNexus the ability to slot in text variables to their storylet content — a very lightweight form of text generation — but it means that a storylet reused several times can vary its internal text so that it is not exactly the same every time the player sees it.

The basic engine code for managing storylets is easy, especially if you don’t need to worry about optimizing for a very large storylet base. The core loop is “go through all the storylets, list the ones whose requirements are currently satisfied, then present a random sampling or a complete list to the player to pick from. When the player picks one, display its content and execute the results, then go back to the beginning.” This is something that one could do in almost any language, and it’s not necessarily to learn a heavy-duty one. Plausible options include Python or JavaScript, or indeed Inform with a Vorple-mediated front end.

From there things get mildly but not horribly more complicated the more of the StoryNexus/Varytale/Voyageur special features you want. Locations can go in as a kind of quality with a different display representation.

Storylet contents could in theory be HTML segments or pages, so if you wanted you could make each individual storylet be essentially a small Twine, or raw HTML/CSS, or something filled in with JS; or you could pull in Tracery to get some custom text effects here.

For opportunity decks, you may want (as StoryNexus does) to have some additional controls on describing how many storylets the player is allowed to draw at a time, which storylets are rare/uncommon/common, whether there are storylets that you can’t discard from your hand or that are mandatory to play, and so on. But still, this is procedurally fairly straightforward.

More challenging is building a nice UI around all that. It’s probably telling that I don’t really love the StoryNexus UI or the Alcyone UI. And as Fallen London has grown, it’s accumulated more and more locations (as alternate storylet-containers), as well as more and more controls for how things can be brought to the player’s attention: colors you can use to highlight important storylets, controls over storylet ordering on the page, etc. This is in my view (with all respect and love to Failbetter) a sign of the system struggling to deal with the fact that there’s a fundamental unsolved UI design question here.

Meanwhile, Voyageur’s UI is cleaner-looking and more handsome, but it’s also streamlined how much the player’s likely to be able to do at a given time. If your world and its possibility space is large, giving the player a list of possible storylets requires presenting and managing a lot of information on a page.

And just the name or description of a storylet isn’t always enough. Often you also want to communicate why the player is allowed to play (or not play) this particular storylet: so for instance “here is a storylet about going to the ball, which isn’t available for you to play now, but will be available once you have Ball Invitation 1.”

This information sets the player up to participate with some level of intention, doing other storylets in order to open up new possibility space. Choosing which storylets to play, in what order, how many times, is the main way the player expresses agency in this kind of system. For that reason, you often want the machinery to be pretty visible, but that leaves the question of how to expose it clearly and comprehensibly.

Fallen London is very explicit about what qualities you need to have to unlock a storylet, what qualities you need to succeed at a storylet, and what your odds are of success. Some people may feel that that keeps the game-ness front and center in an ugly way — which is fair enough — but you also get some special advantages from it. Because the player is choosing when to take risks, you have permission to make bad things happen to the player as a result of their choices, and the player won’t necessarily feel that it’s a gotcha in the same way as if a CYOA branch leads to sudden death.

And it means that the player can plan chains of requirement and fulfillment: I need trade goods to buy a map so I can take ship to Polythreme; I need the Duchess’ approval in order to unlock a social event so I need to be kind to cats for a while first. This enables chains of intentional play. The protagonist of Fallen London can be shaped a lot of ways by your choices, but they’re pretty much always something of a Machiavellian risk-taker who instrumentalizes relationships. The system does a lot to support and reinforce that.

In any case, the UI problem isn’t too bad if you’re writing a fairly modest-sized game with a few hours to a few dozen hours of gameplay, because only a few storylets need to be visible on each page.

But if you’ve got a really huge piece, with lot of storylets with a lot of descriptions and prerequisites, your front end is going to look less and less like a book or a CYOA screen, and more and more like a catalog, complete with categories and paging and specialty items pinned to the top of the page. (And filters? And search functions? Maybe! Who knows!) And then you need all the data and code and layout cruft to support that. This is obviously not hard in the sense of needing new research — many many catalogs have been implemented on the web — but it’s hard in the sense of being a ton of work, and also a design challenge to build this in a way so that it stays accessible and doesn’t make the player more like she’s shopping at Amazon than playing a game or reading a story.

My advice here would basically be to keep it simple at first, but build your system in a sufficiently modular way that you could plug it into a different presentation front-end should you find a need for new organizational hierarchies. Start with the simple-code version and come back to front-end design skills later if you find you need them.

Designwise, there’s more to learn, particularly around design patterns for organizing groups of storylets in pleasing ways. Failbetter has some old posts about this, using names like Carousel and Midnight Staircase, but in practice a lot of the patterns outlined there are used less and less in recent FB content, I think in part because many of them are based around an old expectation of a much more grind-heavy player experience. Assuming you are not writing a browser-based MMO that incentivizes people to pay for extra actions, grind is not a vital feature of quality-based narrative, and some other patterns have emerged since.

Unfortunately, I don’t know of any place where this is written up extensively other than in internal Failbetter discussions. Maybe I’ll do another post just about this at some point.

Building a Salience-based System. In theory this is very similar — again, you have a bunch of pieces of content, which you serve to the player depending on stats or qualities that make particular qualities most suitable or eligible for use. But there are a couple of really important differences that change the balance of responsibility between player and author, and mean that a minimal salience system is necessarily more complicated than a minimal QBN system.

In a QBN, the player is choosing which storylet to do next. In a salience system, the system (or the author by means of the system) is choosing which piece of content to serve next. So that means that you need an algorithm not only to figure out which content is available (easy in QBN) but which content is best to see next, something QBN leaves to the player. This could pretty much get infinitely complicated, but a common approach is to count the number of constraints on each piece of eligible content and then serve the one with the most constraints fulfilled.

As mentioned elsewhere, this can be a pretty fluid system — you build a bunch of default content, then you think “oh, I’d really like a totally different scene to happen here if the player is in love with both Pete and Raccoon,” so you throw in a variant scene with loves_pete and loves_raccoon prerequisites, and it just automatically wins when it’s supposed to. Cool!

Of course, you can wind up with some quirks in practice, especially if not all your prerequisites are equally important. Suppose I’ve written this:

  • Pete love scene — prereq, the player loves Pete
  • Raccoon love scene — prereq, the player loves Raccoon
  • Raccoon love scene, Hat Variant— prereq, the player loves Raccoon and also is wearing a Stetson, so there’s a cute little callback in the scene dialogue

Now if I add

  • Pete & Raccoon Faceoff — prereq, the player loves Pete and the player loves Raccoon

this narratively important moment is no more salient than Raccoon’s Hat Variant love scene, so a player who loves Pete and Raccoon and wears a hat has an equal chance of getting the Hat scene or the Faceoff scene. Now the cute jokey variant you wrote earlier is threatening your major narrative beat. You could either go back and rip it out, or you could add yet another scene,

  • Pete & Raccoon Faceoff, Hat Variant — prereq, the player loves Pete and the player loves Raccoon and wears a Stetson

But now you’re facing exactly the kind of combinatorial content explosion that we were trying to get away from with a salience system in the first place. Gross. A reasonable solution is to do this:

  • Pete love scene — prereq, the player loves Pete; procedural text generates variants with the stetson or no stetson
  • Raccoon love scene — prereq, the player loves Raccoon; procedural text generates variants with the stetson or no stetson
  • Pete & Raccoon Faceoff — prereq, the player loves Pete and the player loves Raccoon; procedural text generates variants with the stetson or no stetson

…keeping a clear separation in mind between core content and the situational rendering of that content. If a variant isn’t going to have any longer-term repercussions beyond the duration of the scene, for instance, that’s likely cosmetic.

The other difference is a little more subtle. The salience-based aspect doesn’t actually specify how the player is exercising agency at all. Whatever choices are being made, they’re either within the content-chunks (as in King of Chicago) or contained in some totally different parallel simulation (as the player navigates the world in Firewatch). So whatever system you’re building has to also provide that interaction context.

Anyway, for this approach I expect you’d need your programming language but also whatever other skills are going to support your player choice mechanic (HTML sections inside salience-picked scenes? a 3D world to explore with messages overlaid?); maybe some procedural text experience with Tracery or something else in order to serve the quality of situationally-conforming text you need. If you want to get fancy with your salience decisions and go beyond counting requirements-met, you might also want a bit of math (read the description of how King of Chicago works and see if it makes sense to you; if no, you might want to add a little more algebra to the mix).

Building a System with Dynamic Requirements. This isn’t something I talked about in the earlier article, but an even-more-complex option is the system where storylets specify roles and cast people into those roles. By which I mean you write

  • Love scene — prereq, the player loves anyone. That person will be slotted into this scene. Details of the scene will be procedurally rendered.
  • Faceoff — prereq, the player loves any two people, at least one of whom is the jealous type. Those people will be slotted into this scene. Details of the scene will be procedurally rendered.

Though of course you can still do the person-specific scenes by specifying the individual by name in the setup.

And here it does start to matter a bit more how you’re coding, because logic programming languages tend to be more designed for this kind of thing than others. Versu did this; Praxis was a logic programming language.

Lighter experiments. I feel like this is a pretty daunting article so far, but actually you can mock up a few nodes like these inside another system and get a feel for what’s involved. Alcyone is a QBN system implemented in Twine. And ChoiceScript provides some of the tools you’d need, too. For instance, say you wanted to play with doing QBN a bit hackily in ChoiceScript, just to get a feel for it.

What I’d do is give yourself a special node, and then within that node, a choice with a lot of selectable_if choices tied to various requirements. Each of those options represents a storylet:

*label qbn_node

  *selectable_if loves_pete #Tell Pete about my love for him. 
    (Requires Loves Pete.)
    *set pete_loves_me true
    *goto qbn_node

Because the node is re-enterable, the player will constantly see whichever subset of options is currently allowed; requirements are expressed in the “selectable_if” line, and results are expressed in *set. Replace *selectable_if with just plain *if in the case that you don’t want that option to be visible until the moment the player can use it.

This is going to get ugly and hard to read quickly — ChoiceScript is designed to cope with a few options per node, not dozens — but if you just want kind of a feel for it, that’s a way to mock up baseline QBN within ChoiceScript.

You could likely also do an opportunity-deck style approach if you needed to, but that would be a bunch more work; I think you’d need to designate a whole other section for setting access variables and then choosing a subset of them to turn on or off. I’d be more inclined to go to Vorple/Inform for this.

Meanwhile, a ChoiceScript salience-esque node would basically score possible next scenes and then go to the one that scored highest: doable, but so fiddly that in the process you lose all the writing benefits that accrue from this kind of system’s easy ability to add new content. But you could still play with the idea if you wanted.

There’s so much else to say about all this, and so few places to point to as obvious resources. But I’ll stop there for now.



12 thoughts on “Mailbag: High-Agency Narrative Systems”

  1. “The basic engine code for managing storylets is easy, especially if you don’t need to worry about optimizing for a very large storylet base. The core loop is “go through all the storylets, list the ones whose requirements are currently satisfied, then present a random sampling or a complete list to the player to pick from. When the player picks one, display its content and execute the results, then go back to the beginning.” This is something that one could do in almost any language, and it’s not necessarily to learn a heavy-duty one. Plausible options include Python or JavaScript, or indeed Inform with a Vorple-mediated front end.”

    In Inform 7 I’ve been using the “Hybrid Choices” extension a lot and this is one of the easy functionalities: Choices leading to other pages can be designated as “for” one or multiple other pages (meaning this choice will show up when the player is on that page), but a page can also have a “choice-switch rule” on it that specifies any number of tests that can disqualify or qualify that choice leading to it to appear on a page depending on game-state.

    Some coding would be involved to display a small selection of qualified choices, possibly by including a random chance in the choice-switch rules, and then a permanent “I don’t like these choices” option that refreshes the page if the player wants to shuffle them.

  2. Coming to this post a bit late, but I’d like to suggest Ink as an interesting alternative.

    Ink on it’s own is pretty powerful by itself, but the really nice thing about it is that the entire Ink engine is available as C# open-source, which makes it very easy to extend with the features you need. Or make use of the Java port (blade-ink, IIRC) or Javascript port (forget what it’s called). Or do as I did, with my “jInk” (also open-sourced) “simply” reimplement Ink script (or whichever script suits you) in your language of choice and extend it with the features you want. In my case, I wanted to be able to access native objects through Ink and not just primitives (e.g., I’d have a person or building represented in the game engine as an object, and would like to call methods on it, such as, in my storyscript), which Ink is not really built to support easily.

    Adding the feature to an established script that otherwise fit my needs was a lot better, IMO, than trying to invent my own. I don’t know how easy Twine would be to extend, but I’d definitely recommend going for that approach, rather than inventing from scratch.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: