A few design lessons I've learned along the way

I realize that few of you “know” me (in case you don’t, I came in #70 in the 2021 IFComp :rofl:), but I’ve now put over a thousand hours into various IF games that I’ve created, and I thought it might be helpful to others if I shared some lessons that I’ve learned along the way.

This series will focus on ChoiceScript (CS) syntax, but it is my belief that many of the concepts and principles apply equally well to other languages/tools.

I’ll start off by declaring right up front that I am not a very skilled coder. About the only language I ever “learned” was some basic HTML for designing Geocities pages back in the day, but nonetheless, it’s hard to make much progress in IF these days without an elementary grasp of how computers think. That being said, there are assuredly more elegant and better ways to do every single thing I will be discussing.

This is going to be a continuing series, so if you find it interesting, please feel free to start Watching this thread and/or adding your own contributions.

MOVE_COUNTER

And now for Lesson #1: move_counter

Even if I have no idea what I’ll do with it ahead of time, I now start all new projects by creating a numeric variable called move_counter which tracks how many steps or “moves” the user makes in the course of playing a game.

*create move_counter 0

If a game consists of exploring “rooms” in a house, for example, then moving between rooms would increment the move_counter variable by one. This gives you (the designer) a rough approximation of how long the player has been playing.

Now, what can you do with this move_counter? Well, a lot:

  • As a stand-alone achievement - Any player who invests enough time and energy to get to 100 or even 1000 “moves” in your game certainly deserves to be rewarded for their dedication!
  • Timed triggers - If the player activates a bomb, you can trigger it to explode in n number of moves, for instance. Or, if an NPC says “I’ll be right back with that ancient map you need to get into the Pharoah’s tomb,” you can use the variable to set a trigger for when they return.
  • Timed movements - Maybe guards switch up their routines every n number of moves, or a store closes/opens, etc. Or maybe a bear only comes out of its den at certain times.
  • Timed gates - Maybe you want players to solve a puzzle to proceed, but in case it’s too hard or difficult, you can have the gate automatically open after n number of moves. Or, vice versa, you can have a gate close after a certain number of moves.
  • Timed payouts - Maybe the player starts a farm or business or trading colony and needs to come back to collect the income. Or maybe the player gets paid a salary every week/month.
  • Timed destruction - Perhaps that loaf of bread or pail of milk will spoil after n number of moves. Or maybe an injured NPC will die after a certain number of moves if they don’t get to a doctor.

Obviously, the possibilities are endless, so code in your *set move_counter +1 lines right from the beginning as you write your game so that you can easily add timed special events later on.

:warning: It’s important to note that when using move_counter as a trigger that you mesh it correctly with a second counter variable.

For instance, let’s pretend that a villainous NPC has planted a bomb and informed the player that it will go off in 10 “minutes” (moves).

"Ha, ha! I've set the bomb to get off in ten minutes, you naive fool. Better run for your life!" says Mr. Misunderstood.

The code that explodes the bomb:

*if move_counter >= bomb_counter
   *goto bomb_explodes

But what state is the variable bomb_counter in before the villain activates it? Normally, my instinct would have been to:

*create bomb_counter 0

But since the move_counter will always be larger than zero, this results in the bomb exploding non-stop, which isn’t what we want :crazy_face: Therefore, I set it up like this:

*create bomb_counter 9999999

And because of that, we need to add one more line of code for when the bomb does explode to reset the bomb_counter variable:

*if move_counter >= bomb_counter
   *set bomb_counter 9999999
   *goto bomb_explodes

Now, going back to when the villain sets off the bomb, we have something like this:

"Ha, ha!  I've set the bomb to get off in ten minutes, you naive fool.  Better run for your life!" says Mr. Misunderstood.

*set bomb_counter (move_counter + 10)

Much more to come :slight_smile:

12 Likes

Sam, great idea to be doing this. You may not claim to be a coder, but what you’re documenting are useful design patterns in IF. It’s hard for beginners to find this type of information.

This is really useful stuff I think, if it can transcend the boundaries between frameworks, and provide an insight into how to design a piece of IF, agnostic of the engine itself.

4 Likes

Lesson #2: dieroll (:game_die:)

Quite literally, the first thing I do when I start a new CS project is add this line to my startup.txt file:

*create dieroll 0

This gives me a numeric variable that I will later use whenever randomization is required.

To understand why this is so powerful, I think it’s worth discussing what the role of a randomization function (*rand in CS format) is in game design.

Without any randomization, a choice IF game is, ultimately, just a sophisticated version of the old paper CYOA books. Yes, there are some ways you can dress it up in fancy clothes with things like stat checks that open/close gates, storylets, etc., but without randomization, anyone who replays your game and makes the same choices will receive exactly the same story.

In contrast, even the smallest and most judicious use of randomization will ensure that no two stories will ever be identical. And, as the author, I love knowing that even I can be surprised by the story’s outcome.

Furthermore, there’s no way to add any motive power or “agency” to NPCs and events without randomization. If everything is plotted out ahead of time, then the NPCs are just as static as those in a (standard, linear) book.

Yes, we’ve all known and read wonderful books and stories with lively, engaging characters in linear books (and IF games), but isn’t it more exciting to have the NPCs be spontaneous sometimes? I certainly think so! :grinning_face_with_smiling_eyes:

Now, CS does let you randomize any numeric value, so what’s the point of having a discrete dieroll variable? Well, I think there are several.

First, with a dedicated dieroll variable, you always have a go-to variable that can be reused multiple times whenever you need some randomization. This really helps avoid the error of accidentally overwriting a previous value for a variable. That’s why I got in the habit of using dieroll for setting even numeric variables that themselves can be randomized.

Therefore, instead of:

*rand strength 4 12

I will use:

*rand dieroll 4 12
*set strength dieroll

And since I never, ever use dieroll as a standalone variable, I can reuse it as many times as I want without having to worry about screwing something up down the line.

In addition, a dedicated randomizing variable (dieroll) will also let you set up some pretty neat sequences that manage how often something occurs.

For instance, let’s say you want to “check” if an NPC traveling with the player is going to pipe up with a comment, but you don’t want the NPC to be chatting non-stop and drive the player nuts. Therefore, you might decide that “one-third of the time, the NPC will say something” is the right benchmark.

*rand dieroll 1 3
*if dieroll = 1
   *goto npc_remarks

And if you want the NPC to chat two-thirds of the time, just do this:

*rand dieroll 1 3
*if dieroll != 1
   *goto npc_remarks

Randomization also lets you do some pretty darn cool stuff such as having an NPC move around on “his or her own.”

Let’s say you have a game where an amateur detective is investigating an old, abandoned mental hospital where some “unspeakable horror” has occurred in the past, Lovecraft style. Cool! And you want the player to randomly encounter a few monsters or NPCs along the way.

First, I assign all rooms or areas a number. If it’s a big universe, I’ll draw up an actual map and write in the room numbers myself so I can keep track of them.

Then, the simplest way to move the NPCs would be Wumpus style where the NPC or monster simply teleports randomly to any given room.

If the mental asylum has 75 rooms or areas, then you just write:

*rand dieroll 1 75
*set npc_room dieroll

But, let’s face it, there are few occasions when an NPC randomly teleporting around makes sense. How, then, can we make it so that an NPC moves around the old mental asylum in a logical fashion, i.e. from one connecting room to the next?

First, I create a scene called room_paths.txt and hand-code all the exits for each room. For example, let’s imagine that room #1 (the front entrance of the asylum) has two exits: either proceed into the main hall (room #2) or go back outside (room #0).

Inside the room_paths.txt, I’ll add this:

*label 1

*set exit1 2
*set exit2 0

*return

This means that, if the NPC is in room #1 (the label), then the NPC has two exits: either Room 0 (outside) or Room 2 (the main hall).

Furthermore, let’s say that the main hall (Room 2) has three different exits: back to the front entrance (Room 1), a flight of stairs going up (Room 3) or the manager’s office (Room 4).

In room_paths.txt, I will write:

*label 2

*set exit1 1
*set exit2 3
*set exit3 4

*return

And so on and so forth. When you’re done, you’ll have a full map of all rooms that the NPC can logically move to from any given room.

But wait! We have a bit of a problem. How do we get the NPC to actually move from one room to the next?

At this point, we know what the possible exits are (from room_paths.txt), but we need to code the line that actually moves him.

Now, in my first game with free moving NPCs, I actually did set it up as a dodecahedron in full Wumpus style, so every room had exactly three exits.

Assuming that npc_room is the variable that determines which room the NPC is currently in, in my main scene file, I could then use:

*gosub_scene room_paths {npc_room}

*rand dieroll 1 3

*if dieroll = 1
  *set npc_room exit1

*if dieroll = 2
  *set npc_room exit2

*if dieroll = 3
  *set npc_room exit3

*return

This tells CS to go to the specific label in room_paths.txt that corresponds to the room where the NPC is at currently, learn which exits are possible, and then re-assign the NPC’s location to the “exit” location.

But that only works if we know that all rooms have three exits. So, we need to add one more step in order to know how to set the randomized dieroll variable to match the number of possible exits.

In room_paths.txt, each room’s information (label) will now look like this:

*label 2

*set exit1 1
*set exit2 3
*set exit3 4

*set total_exits 3

*return

Now, going back to the main scene file, we adjust the code so it looks like this, assuming that no room has more than five possible exits:

*gosub_scene room_paths {npc_room}

*rand dieroll 1 {total_exits}

*if dieroll = 1
  *set npc_room exit1

*if dieroll = 2
  *set npc_room exit2

*if dieroll = 3
  *set npc_room exit3

*if dieroll = 4
  *set npc_room exit4

*if dieroll = 5
  *set npc_room exit5

Of course, if there’s just one exit to a room, this code is overkill :rofl: But now you have a robust chunk of code that can be infinitely reused for to move the NPC from every type of room.

After running the gosub to move the NPC, all you have to do now is check to see if the NPC is in the same room as the player.

*if npc_room = player_room
  *goto npc_encounter

There is a lot more cool stuff you can do with randomization, but I’ll stop here now with an admonishment.

My rule of thumb is, if a random event can happen, it will happen to someone. If you roll the dice and have it affect the outcome of the story, no matter how low the probability (i.e. 1 in 100 or 1 in 1000), it will happen to a user, eventually, especially if your game becomes incredibly popular and cool and famous and millions of people play it :grinning_face_with_smiling_eyes:

And, while it might seem kind of fun and cool to occasionally throw in a dieroll to frustrate the player or simulate “bad luck” or “Lady Fortune smiles upon you” or something, this will not be perceived as “occasional” to the player. Instead, the player is going to say, “WTF is this?” and become frustrated.

Therefore, my advice is to never, ever use a dieroll to simulate luck or chance or fortune. Winning the lottery is fun. Losing it is not. Nobody wants bad luck or misfortune blocking their way, especially if they find out later that it was due to a random die roll. Save that stuff for face-to-face D&D sessions!

Always let the player progress due to his/her choices and skills, not randomized “luck” throws of the dice!

3 Likes

You’re saying that has a danger of ovewriting some variable strength that you’ve already set and forgotten about (but might need later)

So why does this help?

I don’t know. If the player is stuck, occasional lucky random hints may work wonders. Of course, that’s more relevant in Parser rather than Choice. But a high enough Perception skill may yield bonus hints more frequently.

Personally, though, I’d limit luck based die rolls to hints/Perception instead of outcome. Playtesting random games can be extremely tedious. Pseudorandom OTOH…:wink:

2 Likes

I believe he’s advising to keep a specific dieroll variable separate from stats and stick with it for all your die-rolling and variable-changing needs. I’ve done that thing where I forget and made up unnecessary junk variables in multiple passages because I forgot what used before and had all kinds of stuff - $strength $strrand $strengthrand $increasestr $strdiceroll

3 Likes

Lesson #3: The scratchpad (:spiral_notepad:)

Whenever I’m working on a story game, I always create a text-only file (.txt) called, simply enough, scratchpad as a separate file.

Since CS itself is authored by writing .txt files, it’s a simple process to add one more “scene” called scratchpad.txt, but when I’m authoring with other programs, I’ll just use my computer’s built-in text editor to create the scratchpad file.

Just as the name suggests, the scratchpad is a place where you can dump anything and everything without having to worry about it getting lost as a *comment in your game files. And when you’re done authoring the game, you can delete it.

There are several really nice benefits to having this throwaway file that I call a scratchpad.

  • It’s a good place to put design notes for yourself such as “Don’t forget to introduce Mrs. X after the player finds the golden key” or “every NPC speaks using italics but monsters do not” or something like that.
  • It’s a good place to write down in-game specifics that you might forget. For example, in a recent game, I had two dozen fictional corporations that played an important role. I couldn’t always remember their names when I was writing, so the scratchpad served as my reference guide.
  • If there’s a tricky bit of coding syntax that you have trouble remembering, the scratchpad is a great place to store it. For example, remembering where all the parenthesis go in an *if statement that checks multiple variables at once is counter-intuitive for me, so I’ll keep both a blank “check for two variables” and a “check for three variables” sequence in the scratchpad for when I need them.
  • Maybe you wrote a nice sequence of text or dialogue in your game but then realize that it didn’t work in that area, so you’d like to save it for later. Simply paste it into your scratchpad, and it’ll be there waiting for you.
  • The scratchpad is really useful for storing lines of code that you might want to re-use. For example, CS doesn’t allow for arrays, so I usually keep a “blank” chunk of code for when I need to randomly (see Lesson #2) assign values for a variable.

In fact, it’s probably the last use where the scratchpad comes in the handiest for me because I can freely use “find and replace all” on it without having to worry about screwing up my game.

For instance, I might keep this short bit of code in my scratchpad:

*rand dieroll 1 10

*if dieroll = 1
	*set corporation_name ""
	
*if dieroll = 2
	*set corporation_name ""
	
*if dieroll = 3
	*set corporation_name ""
	
*if dieroll = 4
	*set corporation_name ""
	
*if dieroll = 5
	*set corporation_name ""
	
*if dieroll = 6
	*set corporation_name ""
	
*if dieroll = 7
	*set corporation_name ""
	
*if dieroll = 8
	*set corporation_name ""
	
*if dieroll = 9
	*set corporation_name ""
	
*if dieroll = 10
	*set corporation_name ""

Then, if I need to use this in my game somewhere with a different variable, it only takes one second to “find and replace all” the above from the scratchpad and then copy-paste it into my game file at the appropriate location.

By doing the above, it takes me mere seconds to reuse the code instead of having to laboriously rewrite it manually and/or strip out the previous values.

Especially if you start getting into some complex coding situations, it’s really nice to have somewhere (the scratchpad) where you can store the code, use find-and-replace free from the anxiety that you’re going to screw something up, and then copy it over to your working game file.

Note: There’s nothing magical about the name “scratchpad,” so feel free to call your “throwaway” working file whatever you want :smiley_cat:

I really like to use a .txt file as my scratchpad because I have an old computer, so a tiny file that easily loads without some memory hogging app to read and write it is nice. And because .txt files are universal, you can easily access them from any device or operating system. And if, for whatever reason, you need to Bluetooth stream it somewhere or email it to yourself, again, it only takes a second because of how tiny it is.

It’s a relatively small thing, having a standalone scratchpad file, but I find that it makes a really big difference when designing and authoring story games!

4 Likes

I am enjoying your design lessons. You should consider compiling them into an eassy and publishing them. (?)

I use the Scrivener similar to your notes text. Scrivener’s outlining and the ability to break notes into sections related to scenes, characters and objects is very useful. I include it in a separate folder within my “game development” folder.

Thank you

2 Likes

Very kind of you to say this! :blush: Let’s see how they turn out in the end, first.

1 Like

Do you use the csIDE at all or just work in text files?

1 Like

It’s actually called “Author’s Sourcebook”. Kind of encyclopedia of the universe of the story world.

Edit:
Something like "Design Document " for programmers, as applied to writers.

Lesson #4: Ludic weight :weight_lifting_woman:

Ludic weight refers to the impact of the player’s choices in moving the story towards an outcome in a game.

In other words, a choice with heavy ludic weight will dramatically alter and propel a story forward while a choice with a light ludic weight will not really advance the game at all.

On a scale from 1 to 100, a purely “flavor” set of choices would be rated a 1 because they do serve to advance the game to the next page of the story.

"Hey kid, what kind of ice cream do you want?" says Billy.

*choice
   #"Vanilla."
     *set ice_cream_flavor "vanilla"
   #"Chocolate"
     *set ice_cream_flavor "chocolate"
   #"Rocky Road"
    *set ice_cream_flavor "Rocky Road"

"One scoop of ${ice_cream_flavor} coming right up!" says Billy.

Conversely, arriving at a momentous choice that will have a wide-ranging impact on the rest of the game would have a very high ludic weight in the 80s or 90s.

It is of critical importance to understand that there is a difference between the perceived ludic weight of a choice (how impactful the player thinks the choices are) versus the actual ludic weight of the choice, which only the author knows.

After all, perhaps the vanilla ice cream was poisoned! :smiley_cat:

If a player says to themselves “I know the impact of this choice on the game,” then they perceive it as having heavy ludic weight.

Likewise, if a player says “I have little or no idea what this choice is going to do to the game,” then they perceive it as having low ludic weight.

CYOA

The poor design of the original CYOA paperback books meant that the player often had no way of determining the ludic weight of the choices.

Quite often, an innocent-looking choice would lead to insta-death. Or, to put it another way, a choice that appeared to the reader as having low ludic weight actually turned out to have great ludic weight.

These days, people might say “blind choice” to refer to that situation. But whatever you want to call it, a choice perceived as having low ludic weight but is actually a much heavier weight choice is quite an unpleasant experience for the player.

In addition:

  • Having a series of choices in a row perceived as having low ludic weight (whether or not they actually do) will make the game seem boring or slow-paced for the player.

  • Having a series of choices in a row perceived as having heavy ludic weight will come across to the player like riding a rollercoaster - quite thrilling but could become overwhelming if it keeps up for too long.

  • From the author’s perspective, designing choices with a heavy ludic weight usually requires more effort than designing choices with low ludic weight.

  • Nothing is more frustrating to the author than spending a lot of time and effort into adding ludic weight to a choice that the player is oblivious or unappreciative of.

How you want to design the structure of your game is completely up to you, but it’s a good idea to think about the pacing and the rhythm of your choices, in terms of their ludic weight.

If you’re a visual person, imagine a chart showing the relative ludic weight of the choices in your game as they progress in time.

For your story, do you want a gentle crescendo of low ludic weight choices in the beginning that swells like an orchestral movement to a series of Great Choices that determine the dramatic finale?

Do you want a thriller story that lulls you to sleep with low-weight choices but then suddenly hits you with a jump scare of life-and-death choices?

Or perhaps you’ve got the chops to write a three-act story containing a series of building crescendoes and important side stories that all knit expertly together to force the player’s finger to shake as they carefully consider their late-game choices.

Or maybe a walk along the lake is more the rhythm of your story, with several delightful pauses to reflect on the beauty of nature and the mysteries of the human experience.

Whatever you decide, by being conscious of the progression of the ludic weight (both perceived and actual) of your choices through your game, you will be able to ensure that the beautiful story in your mind that you want to share with the world is enjoyable as a game.

5 Likes

Oh yeah, I love CSIDE! :surfing_man:

I’d never heard the term “ludic weight” before. Thank you for introducing me to it.

1 Like

Lesson #5: The Opponent :vampire:

Let me start off by saying that there is absolutely no requirement whatsoever for an engaging choice IF story to have an Opponent.

The inclusion of an Opponent is simply a tool used to achieve choices with a maximum ludic weight (see Lesson #4) score of 100.

In other words, choices made by the player against an Opponent have a maximum score of both “I know what the results of my choice will be” and “My choices are directing the forward progression of the game.”

As such, the Opponent injects the maximum amount of intensity, adrenaline, and excitement into your story, so use it wisely, much like hot pepper sauce :hot_pepper:

An Opponent is anything that serves as a barrier that must be surmounted in order to continue the player’s forward progress in the game story.

A “simple” Opponent is an individual monster, NPC, creature, etc., or a group of individuals. A sword battle against an orc and a game of blackjack in a casino against the dealer are both examples of a “simple” Opponent.

A “complex” Opponent is when the environment itself is what the player must compete against. An example of a complex Opponent would be dropping the player in a labyrinth and telling them to find the exit.

I find that authoring complex Opponents tends to be a lot easier than simple Opponents.

By cranking up the ludic weight of the available choices to 100 with the use of an Opponent, you are, in effect, challenging the player to a battle of wits.

Some folks even call these “skill games.”

Now, as an author, it should be obvious what risk you run when you present the player, otherwise known as your customer, with a challenge that they might not win or defeat and thus fail to proceed further in your story…

…a very frustrated player :angry:

A few compromises clearly come to mind, the first being to simply tack on some kind of side path or backdoor option that circumvents the Opponent.

But knowing that an escape hatch exists dilutes the true ludic weight of the choices in your game and thus (at least partially) deflates the level of excitement the players feel who can surpass the Opponent.

"Don't come back until you have the blue diamond!" crackles Mr. Misunderstood.

Your throat dry as the desert, you realize that your family will die if you do not retrieve the blue diamond for that madman. 

But in order to do that, you need to make some money... fast!

*choice
  #Play Blackjack
    *goto skill_game
  #Ask your rich friend for money
   *goto too_easy

Of course, you can be a bit more subtle than that :stuck_out_tongue_closed_eyes: when including a bypass or backdoor.

For maximum results when using an Opponent in your game story, keep in mind that the player will benefit from:

  • Clear forewarning of what to expect before encountering the Opponent
  • Clear and precise understanding of how the player’s choices will affect the game, specifically including how to win and what will happen when they do win
  • Once the game begins, the player experiences a rising sense of mastery with achievements awarded along the way
  • There is a resounding victory at the end

For example:

"Ha, ha!  Good luck, ${player}, escaping my most complex and dastardly maze yet!" cackles Mr. Misunderstood through the grate above your head.

"I'm going to kill you!" you shout up at the crazed villain.

"Even if, by some miracle, you do find your way to the exit, you'll never get past the dragon, ${player}!  Firebelly will roast you to cinders without a magical amulet to protect you, and you don't have one!" giggles Mr. Misundertood.

"You and me, we'll settle this later.  For now, I've got an appointment with a dragon," you say with a chuckle.

Looking around, you see exits in all four directions.

*choice
   #Go north
     *goto 34
   #Go east
    *goto 23
   #Go south
    *goto 11
   #Go west
    *goto 5

Outwitting and defeating an Opponent certainly fulfills the game component of a story game (aka a work of Interactive Fiction), but you must be careful to not lose sight of the story.

In my silly example above with the labyrinth, the game sequence would be much, much more meaningful if it were tied into the narrative arc of a story.

For example:

  • Why does Mr. Misunderstood have so much animus (hatred) against the player?
  • Why is Mr. Misunderstood threatening the lives of the player’s family?
  • Why is the player uniquely suited to being the target of Mr. Misunderstood’s ire?
  • Why is the player the hero of this story?

If all of those questions (and many more) were answered by the ongoing story in the IF experience, then the “pure game” elements against an Opponent will be even that more satisfying for the player.

2 Likes

I’ll chime in on this to advise: don’t make the author mistake of thinking of yourself as the player’s opponent, developing fiendish puzzles and obstacles specifically to thwart them. Unless you are specifically intending to write a fiendish puzzle game and it doesn’t matter if the narrative grinds to a halt while the player is stuck - but you probably know whether you are or not.

Conversely, I’ve found it works best when the author is on the same team as the player. You should want the player to solve the puzzles and see all of the game. In fact, the most enjoyable experience for the player is if they believe they’ve outwitted the author’s machinations, even if they take the exact planned out path that has been set up for them.

Even better - some of my best experiences have happened when a game designer is subtly one step ahead and helps out. For example, you’ve got a safe and implemented spinning dials the player can experiment with even if they don’t have the combination. When the player finds that scrap of paper with the combination and returns, narrate the player entering the code automatically and opening the safe instead of making them carry it out.

This was best illustrated in the Monkey Island games by Ron Gilbert, et.al...

The player has to acquire two planks of wood and insert them into holes in a tree that spiral up like steps. There are about fifty holes leading up and only two planks, so the solution is insert the first two, climb up, and pull the one behind you and place it in the hole ahead to advance. This could have been a tedious exercise of clicking, but instead once it’s obvious the player has figured out the solution, Guybrush goes “Aha!” and completes the entire sequence without the player needing to actually do it.

The same thing happens on the world map where the player navigates Guybrush in a boat between islands. At some point the game understands the player has all the coconuts they need (or whatever) and when you jump in the boat to navigate back, there’s an interstitial title “After lots more furious paddling…” and the game transports the player where where they need to be instantly to continue. I really appreciated those design decisions.

5 Likes

I am probably in the minority with this, but I love entering the code after I’ve figured it out. But then I also love making my own maps, and I know most players don’t. It’s a fine line for me as a player in what implicit actions enhance the game and what implicit actions feel like they’re stealing my agency. I guess we all write what we like to play and how we like to play, and it’s a given that others will be grumpy about some of that.

6 Likes

Definitely valid. All the more reason for authors to consider implementing an “Easy Puzzles/Would You Like Hints and Assistance?” mode that the player can choose. Or at minimum have the game monitor what the player is doing and nudge them in the right direction when it seems they are stuck. If the player has entered the wrong code to the safe ten times while holding the scrap of paper with the combination, offer a gentle nudge “Hey, didn’t that piece of paper in the Laboratory have three numbers on it like a combination?”

5 Likes

Lesson 6: Game vs. Story (:crossed_swords: v. :books:)

When it comes to elucidating the elements of IF design, no two terms can be more frustrating than “game” and story" because of their vague and broad definitions.

And because Interactive Fiction (IF) itself is a surprisingly slippery genre to define, it’s pretty easy to get lost in metaphysical clouds when you’re dealing with core terms used in story-game (IF) design.

Therefore, please understand that my definition of game and my definition of story are just that - my personal definitions.

Precisely because written stories can only be told through text, there are plenty of texts written about story structure and design, most of them authored by people far smarter than I. If you want to know how to put together a good story, consult those resources.

For the IF author, you’ve got to do all that and more because as soon as you want to add any ludic weight to the choices (see Lesson #4) or “add some game to your story,” your responsibilities become considerably more complex.

And since I think it’s fair to say that most choice IF authors begin with a story (or story idea) rather than a game (idea), it’s worth taking a deeper dive into how to “gamify” a story and make the most out of the play/read experience that is IF.

How exactly do you integrate a story and a game in a satisfying way?

Storylets

This is the option that companies like Failbetter Games (Fallen London) have gone with.

In a storylet structure, the stories are all self-contained, so the game that the player is playing is encountering storylets in random order and then using their brain’s built-in heuristics to invent an internal backstory that knits the storylets together into a cohesive whole.

In order to prevent too much incongruity between the sequence of storylets encountered by the reader, the storylets can be categorized and labeled (gated) ahead of time by the author(s) so that there is a lower risk of breaking the illusion of narrative coherency.

This type of IF design has a very high ludic weight score for choices, making it very “game-like.”

The downside for the author(s), however, is that the scope of the stories that can be told by the author(s) is limited. And if the storylets are gated too narrowly, the gameplay experience is diminished.

Schrodinger’s Cat

The original CYOA paperbacks were all Schrodinger’s Cat stories, where all possible narrative paths were written ahead of time and remained in a “superposition state” until they “collapsed” as the player made choices.

The most common form of choice IF works these days is of the Schrodinger’s Cat variety.

The downside for a Schrodinger’s Cat IF work is that it is really hard to add ludic weight to the choices. And if the choices become too lightweight, the game risks coming across as boring or superficial (or a “railroaded” linear story).

A “flavor” choice (one with little or no effect on the outcome) is easy to author since it doesn’t deviate from the narrative path of the story that you want to tell. But a choice with a lot of ludic weight means you need to build a new narrative path, and that means writing another version of your story (each in its own alternative universe).

Therefore, the more ludic weight you give the choices, the more work you have to do. And now, instead of writing just one good story, you have to write several which is why these types of games usually have the highest word counts of any type of IF.

It’s a monumental task, but some authors definitely relish this type of deep dive into a story world that they want to share. JK Rowling never authored any IF (as far as I know), but knowing that she spent years creating the world of Hogwarts in her head tells me that she’s probably just the type of author who could write an excellent 1-million-word choice-based Schrodinger’s Cat IF.

If you like to write, and you want to tell a lot of stories, go with the Schrodinger’s Cat format.

Segregation

Perhaps the easiest way to integrate story and game is to keep them walled off in their own separate spaces.

Often, the Segregated IF work starts with a story that introduces and sets up a game. The story is then “paused” while the player plays and then wins a game, at which point, the next part of the story is told, and so on and so forth.

Although it’s a parser game (parsers have their own design issues that won’t be discussed in this series), you can see the Segregated format put to excellent use by this year’s winner of IFComp.

That game begins with a story (that’s frankly too short), then there’s a quick game, then it goes back to the story, and so on. In addition, the author cleverly included the outcome of the games themselves to further the narrative arc of the story.

Probably one of the most famous and beloved of all choice IF games is Tin Star, and it contains plenty of “side quest” games Segregated from (but in harmony with) the story.

Regardless of how you want to weave them together, keeping your game elements discrete from your story elements makes authoring (and coding) your IF game story a lot simpler.

If you’re taking the Segregated approach, my advice is to focus your efforts on making sure your story works because even the dumbest and simplest games will be more than engaging enough.

Text Arcade

As far as I know, only me and a handful of other people have ever attempted this format.

A Text Arcade is where there is no story at all, or only the merest fragment of a story. Instead, the ludic weight for all choices is cranked up to 100, and the player does nothing but play games.

It’s only because there are no graphical elements, and the game plays out entirely through numbers and words printed on the screen, that a Text Arcade even qualifies as IF, and it’s on the fringe.

However, I’m including Text Arcade format because it is the logical extension of what happens if you eschew story for maximum game in a text-based format.


Take some time and think back on some of your favorite and least favorite IF games and pay attention to how their story elements worked with their game elements. This will help you better understand how to structure your game-story to enhance the player’s experience.

3 Likes

Failbetter, in their Storynexus design document, referred to this as “fires in the desert” design - if you can imagine story content as visible fires in a desert at night, and the paths to them as blackness in between, it allows the player to infer and imagine what happened to connect one fire and section of story content to another.

This is where QBN (Quality-Based Narrative) comes into play. Despite the overall random structure of the story, the player has “qualities” (variables) that weight what choices are randomly available at any given time, as you’ve said. There may be a choice of ten storylets available for the player to “draw” (Storynexus and Fallen London use decks of cards as a motif), but if the player isn’t carrying a lot of money, the “thieves attack!” story may be removed from consideration. Or if the player needs healing, that may add a “wandering physician” card to the deck, which can weighted to appear more consistently depending on how much the player is injured. Or if the player has already visited the Ruins, they won’t get a story with a character who sends them on a quest there.

4 Likes