A few design lessons I've learned along the way

Lesson #7: The Gate :door:

A gate is any obstacle or impediment that prevents the player from further progressing along a game-story path until it the gate is overcome.

Gates are what separate a paperback CYOA from a computerized choice IF game because gates move. Gates are binary in nature - either on/off or closed/open (or up/down, etc). Gates “move” by either closing or opening.

Gates are one of the most powerful tools in the IF author’s arsenal, but it is important to understand that they can be used in two different ways.

Gates as Game Elements

When used as part of the game element of an IF work, the gate serves as an Opponent (Lesson #5).

The player must match their wits against the Opponent until the Opponent is defeated, at which time the gate is opened/activated and the player can now continue progressing along the game-story path.

Parser people often call gates “puzzles” or “fetch quests.”

Common gates used as game elements include:

  • Find a physical key to open a door
  • Solve an intellectual challenge to unlock a lock
  • Kill a monster/NPC that is blocking off an area of the map
  • Find a tool/device required to access an area of the map
  • Find/obtain an object and use it to bribe someone
  • A series of gates daisy-chained together to serve as one “mega gate”

Etc, etc.

Choices made by the player to defeat/overcome the gate have a very high ludic weight and therefore add a boost of adrenaline to the game-story.

Most authors find that using gates as part of game design is a fairly intuitive process.

Gates as Story Elements

Usually, it’s when gates are used as a narrative device that authors can run into trouble.

When telling a story, the author’s intention is to present a narrative to the player, so the story gate is used to improve the narrative cohesiveness of the story.

In other words, story gates allow the author to tell a better story than the one that could be told without it.

The easiest way to use gates as a story element is to tightly bind them to your narrative arc. In other words, each segment of your story is one “level,” and you can’t “level up” until you’ve passed through a certain portion of that level.

For example, if you were writing a murder mystery, you might use a story gate to ensure that the player (a consulting detective) finds some important clues before moving on to the next room in the mansion.

Managing story gates when they are tightly structured like this is straightforward for the author because it compartmentalizes the story into “acts” or “chapters” or “cohesive storylets” while still giving some freedom of movement (and thus, ludic weight) to the player’s choices.

However, where it is easier for the author to get bogged down is when the gates are more flexibly arranged. Many people refer to this type of gate structure as a “stat check.”


Imagine you’re writing a story where the player is unfairly imprisoned in a castle by an evil duke and must escape. Earlier choices made by the player have affected their “stats,” and now you want to put in a story gate that references those stats.

The hideous cackle of the duke's laughter still ringing in your ears, you know that you need to get out of this dungeon and back home to protect your family as quickly as possible.

But how?

*choice
  #Sneak out the window
    *if stealth >= 50
     *goto sneak_successful
    *else
     *goto caught
  #Attack the guard
   *if strength >= 50
     *goto attack_successful
   *else
     *goto caught
  #Cast a spell of Opening
    *if magic >= 50
     *goto magic_successful
   *else
    *goto caught

Oops!

This story gate has no option for the player who has not achieved any of the “stats” and so the player cannot progress further in the story.

And while you might catch this error yourself, there is no diagnostic tool that exists (that I’m aware of) to find the ones you missed. Therefore, always be extremely careful when using elastic gate structures.

:warning: It gets even more complicated when you have dozens of simultaneously moving stats to keep track of and then design gates for. Use “stat checks” exceedingly sparingly for story gate purposes!

But look what happens even if you do include a path for players who don’t have the requisite stats:

The hideous cackle of the duke's laughter still ringing in your ears, you know that you need to get out of this dungeon and back home to protect your family as quickly as possible.

But how?

*choice
  #Sneak out the window
    *if stealth >= 50
     *goto sneak_successful
    *else
     *goto caught
  #Attack the guard
   *if strength >= 50
     *goto attack_successful
   *else
     *goto caught
  #Cast a spell of Opening
    *if magic >= 50
     *goto magic_successful
   *else
    *goto caught
  #Meditate in your cell
   *goto thinking_time

Now you’re telling a story of sitting around and doing nothing in the middle of an action-adventure story about beating up guards, casting spells, and sneaking out of a castle dungeon :face_with_raised_eyebrow:

And the only reason that you’re telling it is becase you forgot that the purpose of a story gate is to help you tell a better story.

Never let a gate force you to tell a story that you do not want to tell!

Back to the example above about the player in the dungeon, let’s say you, as the author, want to tell a really engaging story along the thief path about how cool it is to be able to sneak around undetected, filch jewels out of people’s pockets, etc.

And so you put the player through a bunch of paces in which they master the art of being a thief. But then they got caught and thrown into the evil duke’s dungeon.

  • How does it help you tell a better thief story if you inform the player about the fighter (attack the guard) and wizard (cast a spell) options, especially if the player can guess that they’re probably not going to work?
  • How does it help you tell a better thief story if the player actually tries one of these “off-path” options and then fails to progress?

It probably doesn’t.

But something like this might:

The hideous cackle of the duke's laughter still ringing in your ears, you know that you need to get out of this dungeon and back home to protect your family as quickly as possible.

But how?

*if stealth>= 50
   *goto thief_menu
*elseif strength>= 50
  *goto fighter_menu
*elseif magic >=50
  *goto wizard_menu
*else
  *goto noskills_menu

*label thief_menu

*choice
  #Try to escape out the window
     *goto window_escape
  #Find something to pick the door lock
    *goto door_pick
  #Search for a loose stone in your cell
    *goto stone_finder

Woah!

Now we’re truly telling a better (and fuller) thief story because we’re no longer wasting time with the wizard and fighter paths :smiley_cat:

Mind you, there is no one correct structure for a story gate. What matters is that the author asks “Is this gate helping me tell a better, more cohesive story?” before adding it.

2 Likes

Yah, it’s a pretty cool ludic system, I admit.

Just provide an option to loop back and grind levels. Works on paper, too.

Lesson #8: More fun with Randomization :game_die:

Besides having moving Gates (Lesson #7), the other advantage (computerized) IF has over linear fiction is the ability to introduce randomness into a game-story.

Randomization ensures both variety and spontaneity during subsequent replays (re-reads) for players as well as a lovely unpredictability for the author.

The judicious use of randomization can really add some spark and pizzaz to your game-story.

"Simple" Opponent

You cannot have a simple Opponent (Lesson #5) without randomization.

Something as elementary as a game of Rock-Paper-Scissors requires randomization to exist - the author simply cannot “pre-write” the choices that the Opponent is making.

Other Game Uses

There are hundreds of other ways to implement randomization in the game element of your story, including:

  • Setting up initial stats
  • Prices
  • Rewards/treasures

Etc, etc.

Mad Libs

On a story level, one way to add some color and humor is to use some Mad Libs.

:warning: This will be far more effective if you do not tell the player that the text they are reading has been “Mad Libbed.”

Effective texts for the Mad Libs treatment include:

  • Names of secondary (or even primary) NPC characters
  • Place names
  • Business establishment names
  • Comedic or humorous dialogue/monologue
  • invented words used in-universe
  • Cursing/shouting/obscenities
  • Exclamations of joy/happiness
  • Catch phrases
  • Insults
  • Job titles/descriptions
  • when you need to repeat text but want to keep it feeling fresh

And many more…

If you’re doing this in ChoiceScript (CS), the author’s handiest tool is the Scratchpad (Lesson #3) where you can set up your Mad Libs “templates” and then copy-paste them into your scene files where needed.

One way to compose Mad Libs is by writing an ordinary paragraph of story text:

"Good day!" exclaims the footman.  "People of your lowly station are certainly not welcome in Burleigh Hall.  Begone with you, you stinking beggar!"

"I say!" you declare before turning around and stalking off into the rain.

And then go through and highlight which words or phrases you think might be amenable to the Mad Libs treatment. These will then become (temporary) variables which the IF engine will then randomly choose to display to the player.

*temp good_day ""
*temp exclaims ""
*temp footman ""
*temp begone_with_you ""
*temp "stinking_beggar ""

(random dieroll chooses each variable's values)

"$!{good_day}!" ${exclaims} the ${footman}.  "People of your lowly station are certainly not welcome in Burleigh Hall.  $!{begone_with_you}, you stinking_beggar!"

"I say!" you declare before turning around and stalking off into the rain.

After that, you just roll the dice (Lesson #1) n number of times for however many synonyms or silly alternative words/phrases you want to have for each Mad Libs variable.

Indeed, sometimes it’s worthwhile to Mad Lib (randomize) an entire line of dialogue or even a whole paragraph of text.

Don’t forget that you can also apply the Mad Libs treatment to numbers as well:

*rand dieroll 41 87

"Hey, my grandpop was a grass farmer on Herklion V for ${dieroll} years!"

Gating

One structure an IF author can use is implementing randomized Gates to deliver storylines.

Perhaps you have several storylines developed that you want the player to explore sequentially, but you want each storyline delivered randomly without repeating.

For example, the player buys a newspaper. You want to present the “articles in the newspaper” to the player in a randomized fashion without repeating any.

This is a Carousel type randomized gate:

Opening the newspaper, you scan the pages until you spy an interesting article:

*label topic_decider

*if topic1_told = true
   *if topic2_told = true
     *if topic3_told = true
       *goto topics_done

*rand dieroll 1 3

*if dieroll = 1
  *if topic1_told = false
     *set topic1_told true
     *goto topic1
  *else
    *goto topic_decider

*if dieroll = 2
  *if topic2_told = false
     *set topic2_told true
     *goto topic2
  *else
    *goto topic_decider

*if dieroll = 3
  *if topic3_told = false
     *set topic3_told true
     *goto topic3
  *else
    *goto topic_decider

*label topics_done

With a sigh, you close the newspaper.

In CS, keep in mind that temporary variables don’t carry over to other scene files, so you’ll have to decide where is the best place to declare them.

Another way to use randomized Gates is to build a randomized Carousel right at the beginning, much in the style of the old CYOA paperback “The Cave of Time.”

That story structure has multiple big branches at the beginning, hence adding a lot of ludic weight to the early choices. Each of these early branches then develops into a full-sized story.

Likewise, any story-game with a hub-and-spoke design could have those spokes randomly accessed in a Carousel fashion.

NPCs

Besides just choosing the names of NPCs, you can add a lot of personality and character with the judicious use of randomization.

For instance:

  • Greetings
  • Personal history
  • Catchphrases
  • Gender and sexual orientation
  • Romantic inclination (including lack thereof)
  • Personality type
  • Looks/physical characteristics
  • Preferred conversation topics

Besides just adding some nice “flavor” to your story, randomized NPC elements can also determine things like:

  • Will/how will the NPC help the player?
  • Will the NPC follow the player around (join mission)?
  • Will/how will the NPC attack/run away from the player?
  • Will/how will the NPC reciprocate a romantic relationship with the player?
  • What kind of topics does the NPC want to discuss?
  • What kind of topics does the NPC want to avoid?
  • What does the NPC do when the player isn’t around?

And, as previously discussed, randomization can tell the NPC when and how to physically move around inside the story-game’s map.

With some effort, the author can make the encounters with NPC quite dynamic and life-like with randomization.

Gamesetup.txt

Sometimes, you may want to randomly allocate resources and settings before the the player begins pressing buttons.

Perhaps you want to randomize a layout of the map, the location of certain items or NPCs, or initial stats. You may also want to “roll and generate” the NPCs themselves, choosing their stats, personality, and other characteristics.

If so, my recommendation is to create a separate standalone scene file called gamesetup.txt.

This will give you a place to include your (potentially quite lengthy) code that uses randomization to “initialize” the game-story without clogging up your main scene files.

When your game-story begins, have it run the gamesetup.txt scene once and then proceed to the main scene file.

Keeping it Fresh

The wise use of randomization keeps your game-story feeling fresh, dynamic, unpredictable, exciting, and engaging.

Randomization makes every playthrough an equally compelling adventure for enthusiastic veterans and newcomers alike.

NPCs can be created which are far more life-like, responsive, and authentic with the right use of randomized components.

Indeed, NPCs can even be created which move about and affect the environment “on their own” within a story, with the use of randomization.

Repeated chunks of dialogue or text can be given new life with a fresh coat of randomized paint, and latent humor can be injected into a scene with the Mad Libs treatment.

And delivering storylines in a randomized Carousel fashion can add some lovely complexity to the player’s path through the narratives.

Last, but certainly not least, for choice IF authoring languages such as ChoiceScript, randomization is the only way to add most of the pure game elements to your game-story.

2 Likes

I’m really glad that you made this comment.

If the goal of your game is to make the player grind (for a while), then the grinding loopback option may be what you want.

However, if you’re trying to tell a story, or three parallel stories of being a thief, a wizard, and a fighter, then looping back and grinding probably isn’t something you want to include in your stories because it’s rather boring to read about.

By refocusing on the fact that the author wants to tell the story of being a thief, the gates are then arranged in such a way as to enhance the telling of that story.

In this way, you never have any “dead ends” because the stat checks exist to further the story, not determine a game that entails grinding through different levels.

From the outside, the text of the choices doesn’t look all that much different whether it’s a game or a story element. What matters is how the choices are structured by the author based on what the author wants them to achieve.

At any given moment, the author is implementing choices to either tell a better story or make a better game for the player to play.

1 Like

All the more reason for authors to consider implementing an “Easy Puzzles/Would You Like Hints and Assistance?” mode that the player can choose.

I have definitely always admired @aaronius’ strategy in his interactive novel Blue Lacuna where he checks how many times people “examine” objects and then gives a simpler, or more challenging puzzle set based on the result.

The relevant source code:

4 Likes

Yes. it is a very lovely parser game, indeed.

In this thread, however, I was trying to focus on game design for choice-based IF.

1 Like

Yeah, I was just highlighting a method for setting variable difficulty. He checks if they checked their own inventory, and if they examine a certain number of items. You could certainly do something similar in a choice-based game!

3 Likes

Lesson 9: The Stat Screen :chart_with_upwards_trend:

In ChoiceScript, the Stat Screen is a perennial option available to the player from the top menu, but many other choice-based IF use some form of “inventory” or stat screen.

Once you understand the difference between game and story (Lesson #6), you will quickly see that the Stat Screen is almost always used as a game element.

With the experience on the main screen (usually) being about story, the Stat Screen being a game element sets up a nice contrast. Exploiting this contrast can help you create a more interesting and engaging experience for the player.

Stat Screen as Story

Generally speaking, if your main screen is about story, then you don’t want your Stat Screen to also be about story as well.

Therefore, expository passages about the story in the Stat Screen should be avoided. If you’re presenting the player one story in the main screen, a secondary story (in the Stat Screen) about the first story will be rather confusing and dull.

Unless you want to go “meta” and turn a mini-Story about a Story into a game element, don’t include story elements on your Stat Screen.

Note: The one exception would be when the main screen is a game. Then some story elements would be welcome on the Stat Screen.

Stat Screen as Game

Since most choice-based IF presents the player with an experience of progressing through a story, the purpose of the Stat Screen becomes clear: to inform the player of how they are controlling the story (i.e. playing the game).

In other words, the game that the player is (usually) playing is “how can I guide this story?” and the Stat Screen’s role is to show them how well they are (or are not) guiding the story.

Numeric valuables/stats

One really easy way to do give game feedback to the player is with stats (numeric variables).

Much of the time, numeric valuables (stats) are being used by the author to track the player’s choices in order to select for Gates (aka “a stat check”).

Yet “stats” all on their own, without ever needing to be “checked” (Gated), serve as an excellent way to give feedback to the player on the Stat Screen on how they are controlling the story.

Furthermore, ChoiceScript comes with a built-in graphical bar chart option to make those “stats” more eye-catching and fun.

As an author, if you are going to track stats primarily for the purpose of giving feedback to the player on the Stat Screen (and not for Gating), then track the stats early and track them often.

In fact, track every single story choice, if you can. If you’re using ChoiceScript, “fairmath” is perfect for keeping kinds of “stat” variables within bounds.

Player Customization

Most CS games are authored in such a way as to let the player customize certain elements of the story.

The most common of these is the player’s (in-game) name, but other customizations include physical attributes, gender, romantic orientation, and “character class.”

Generally speaking, since these player-initiated customizations are part of the game element of your game-story, they should be displayed on the Stat Screen.

Mileposts

Another type of game data that you can provide on the Stat Screen is milepost information.

Milepost information is any kind of landmark reference that helps the player know where they are in the story. Milepost information usually comes in the form of a string (text) variable rather than a numeric variable.

For example, the player’s current physical location is milepost information that you could use on your Stat Screen:

[b]Current location[/b]: ${player_room_desc}

Other milepost information might be about the last important node that the player has passed through in your story.

These could be the little “reveals” at the end of a chapter or scene. Or they could be brief overviews of what important information the player has just learned or what’s going on in the story.

[b]Current location[/b]: ${player_room_desc}

Right now, you are ${mission_desc}.  

${move_counter} days have passed since you set out to ${mission_title}.

You have just learned that ${recent_discovery}.

Whatever the content, remember that the purpose of providing milepost information on the Stat Screen is to help keep the player oriented inside your story.

Player’s Memory

If your story is sufficiently complex, you may need to assist the player by displaying memory aides on the Stat Screen.

For example, for a detective story, you may want to keep a log of all of the revealed clues.

However, the most common form aiding the player’s memory on the Stat Screen is the inventory list.

Hand coding an “inventory list” can be a bit challenging for a beginning CS author, but luckily, there are several good examples online of how to do it in an elegant and dynamic way.

Situational Stat Screens

There are several occasions for when you might want to present a different Stat Screen to the player based on their current situation.

For instance, at the very beginning of the game-story path, you may not want to reveal to the player any stats that could be a “spoiler” for an upcoming narrative arc.

:warning: My understanding is that iPad CS players have the Stat Screen permanently visible in a side window.

Luckily, the ability to display different Stat Screens is easy to code in CS by using variables exactly the same way you would do in any regular scene file, with one key difference.

For example, let’s say that you are authoring a story where the player is a young royal who must choose between raising an army to seize the throne and going abroad to raise money via diplomatic alliances.

*if army_path = true
 *goto army_stats

*if diplomat_path = true
  *goto diplo_stats

*label army_stats

You are marching to ${castle}.

Current army strength:

Infantry: ${infantry_number}
*line_break
Archers: ${archers_number}
*line_break
Cavalry: ${cal_number}

*goto finish

*label diplo_stats

You are traveling to ${destination} to meet with ${other_royal}.

So far, you have raised ${funds_raised} golden ducats.

Alliances forged: ${alliances}

*goto finish

*label finish

As you can see, each situational stat screen set of data ends with a *goto finish that jumps to the very bottom (*label finish) and then ends without anything happening.

In CS, this allows the Stat Screen to present a normal “Return to game” button at the top left of the player’s screen. You can, alternatively, put code at the end of your Stat Screen using the standard *choice or *fake_choice commands.

Multi-page Stat Screens

Adding a *choice or *fake_choice at the end of the Stat Screen code allows the author to offer multiple pages “inside” the Stat Screen that operate independently of the story unfolding on the main screen.

Generally speaking, most game-stories do not have enough progress “stats,” milepost information, and memory aides to take up more than one screen’s worth of space.

However, if you do need to display information across multiple screens, the CS commands work exactly as they do in main screen files, including the ability to run a subroutine from a different file (*gosub_scene xxx) or jump to a different scene file (*goto_scene xxx) altogether.

In effect, the Stat Screen can let you nest a second game-story inside the main story. An enterprising author could certainly put that to good use!

But most of the time, keeping the Stat Screen slimmed down to a single page provides a more engaging experience for the player.

1 Like

He checks if they checked their own inventory, and if they examine a certain number of items. You could certainly do something similar in a choice-based game!

Ah, now I understand! Yes, thank you. That’s an excellent idea for game design.

Lesson #10: CoG House Style :house:

Now that we have a firm grip on the design elements and structure of a choice-based work of IF, it’s time to apply these to Choice of Games’ house style.

The COG design “style” is only required for all in-house games, but it has an outsized effect on the design of most of the independently authored games written in CS as well.

The 80% Rule

Of particular note from a design standpoint is the requirement that every player must successfully make it through at least 80% of the narrative arc (story).

In other words, no matter how “poorly made” the choices are by the player, the player will still get a full-sized story.

This one rule alone serves to squelch the option of including game elements in a CoG house style story-game.

The possible risk (and, therefore, ludic weight) of making a choice evaporates if the player knows ahead of time that “all roads lead to success” or a “guaranteed win.”

From what I understand, the founder of CoG had been “burned” as a child by some of the comically overweight ludic choices in the early paperback CYOA books.

Therefore, all CoG games do the opposite - erase (nearly) all possible risks for the “player.”

Gamifying a CoG House story

Adhering to the CoG house style forces the author to make two very important design decisions at the beginning of a project: how to gamify the story and how to write an excellent story in Schrodinger’s Cat style.

Thankfully, since there is ample coverage of both how to write a (conventionally linear) story as well as structure a Schrodinger’s Cat story, we can concentrate here on ways to gamify a CoG house rules story-game:

  • Randomized story gates
  • Mad Libs text
  • Effective use of the Stat Screen
  • A “complex” Opponent
  • Inserting (true) game content as skippable sidequests/ “minigames”
  • Dynamic (randomized) NPC interactions
  • Signpost “best/most desirable story” routes

In other words, nearly all of the design elements of choice IF are available to the author of a game-story that fully adheres to CoG house style.

The only thing that would be outright prohibited would be including a true game in the middle of a story, one that requires the player to earn a (genuine) win in order to finish the story.

Spinning the gossamer web

Is it a tall task to write a compelling story across several different and often parallel narrative arcs, keep accurate tabs on the player’s progress, and create mechanisms to customize the player’s story track(s)?

Yes, it is.

And it is even taller of a task to then take that multidimensional narrative framework and embed it with game or “gamified” elements throughout so that both the story and game elements work in harmony to create an exciting and engaging experience for the player?

Of course. But I, for one, think it’s worth the effort.

Stay the course, authors!

:writing_hand: Sam

2 Likes

I think I can understand this. The audience for CoG are looking for an “interactive novel” instead of a game-book that can possibly shut them down and make them start over multiple times. Marketing wise, it makes sense that people may be willing to pay for a novel-length story they can read to a conclusion as opposed to a game they might get stuck 20% of the way into.

Design-wise this means an author is required to create plot instead of puzzles. In a way, it kind of mirrors the Failbetter philosophy that failure should be interesting, not a game over.

Hosted Games do not require the house style and have fewer requirements.

4 Likes

Yes, indeed.

I guess what I’m trying to say here is that CoG’s “all story/no game” house style tends to lead to game-stories with a high number of choices with low consequences (low ludic weight) and thus a high risk of becoming boring/superficial to the player.

Using all the techniques I was talking about, however, it IS possible to add some “gaming excitement” to a novel-length story without breaking any of CoG’s house style. Even if you aren’t required to follow the house style, it is what most CoG players expect.

As for Failbetter, their storylet design structure comes built-in with a lot of ludic elements and so excels at turning non-sequiturs and failures into interesting “emergent” stories.

I can understand that thought. When I first checked out CoG I likened the player agency to “I send the character a postcard every once in a while and they get around to it eventually” in jest. But I think what it comes down to is narrative scope and focus.

While parser games work really well with physical puzzles involving inventory and items and locks and keys, the CoG house style lends itself well to long-term narrative - instead of figuring out how to frob the foo with the bar, the decisions made by the player might affect the political structure of an entire kingdom over years and decades or foster a long-term relationship. It depends on the reader’s preference on what they find compelling, and I’d venture that “ludic weight” is relative based on the scope of the story.

5 Likes

Lesson #11: The limitations of CS

ChoiceScript is a rather flexible and powerful language/tool for writing choice-based IF, but it does come with a few important limitations as well as a few unusual quirks.

Trouble with Decimals

Most of the time, CS authors are using numeric variables that are exclusively whole numbers, most commonly 1-100.

However, it’s when you want to start performing arithmetic on numbers with decimals that you can run into trouble.

For instance, CS gets a little nuts when handling the number “0.1”

*temp number 0

*label test_run

*set number + 0.1

The current number is ${number}.

*choice
   #Add another 0.1
    *goto test_run

Logically, you should get “0.1” and then “0.2” and “0.3” and so on and so forth, however, CS loses its marbles and will start giving you results like “0.399999.”

Oddly, though, adding or subtracting “0.25” seems to work just fine. There are probably other decimals that cause CS to go wonky, so be sure to test them thoroughly and make sure you really need them.

Always Be Updating

If there’s anything that’s moving in your story, whether it be an NPC or a story Gate, CS forces you to manually update the status of every one of those things every single time it changes.

For example, let’s say you have a game-story where the player can take off their cloak. This cloak will then be located forevermore in the room where they “dropped” it until they pick it up again and wear it.

This means you need to track:

  • If the player is wearing the cloak (or not) - inventory
  • Where the cloak is (but only if not being worn by the player)
  • The cloak’s status as “pick-uppable” or not (because it’s being worn).

You are in the guards’ room of the upper dungeon.

*if (cloak_loc = player_loc) and (cloak_wearable = true)
   You see a warm cloak here.

*choice
   #Leave
     *goto room_14
  #Pick up the cloak
    *goto add_clothes

*label add_clothes

You pick up the cloak and drape it over your shoulders.  

*set cloak_wearable false
*set cloak_loc 0
*set inventory &" a warm cloak"

All of the above is fine for a single item, but it can get quite complex and lengthy if you’ve got dozens of different items/NPCs (and the state of those items/NPCs) to update any time there’s a change of status.

Subroutines

I’m really not much of a coder, so I cannot tell you why this is so, but my experience as an author is that, if you have too many subroutines in your story-game, it will slow down the CS “engine.”

As a result, the player will get (sometimes brief) interruptions in their gameplay as the screen says “loading…” while the CS engine runs through all of its subroutines.

How many subroutines are too many for the engine to handle? I cannot give you a hard number. But I will say that it’s not a particularly large number.

This makes it very tricky to create complex games involving multiple items/NPCs updating simultaneously, but it can be done.

Your best bet for complex games is to trim your code down as “lean” as you can.

I’ve also found that running *gosub routines inside one scene file rather than *gosub_scene routines that switch back and forth between scenes (your .txt files) execute more smoothly and rapidly.

Scene length

Even without subroutines, CS can bog down a bit if a scene (.txt file) is too long.

My recommendation is not to go much past 2,500 lines of code in any one scene.

Arrays

Perhaps the most painful limitation of CS is the absence of arrays. If you want one, you have to use a workaround.

An array, in this case, is a list of possible values for a variable.

For example, here is some Inform 7 code used to randomly select a greeting spoken by an NPC:

"[one of]Greetings[or]Hello[or]Hi[or][Extended Hello][at random]." To say Extended Hello: say "[one of]Such a pleasure to make your acquaintance[or]I'm delighted to meet you[at random]".

And here’s the equivalent code in CS:

*temp greeting ""
*rand dieroll 1 4

*if dieroll = 1
  *set greeting "Greetings"

*if dieroll = 2
  *set greeting "Hello"

*if dieroll = 3
  *set greeting "Hi"

*if dieroll = 4
  *rand dieroll 1 2
  *if dieroll = 1
     *set greeting "Such a pleasure to make your acquaintance"

  *if dieroll = 2
      *set greeting "I'm delighted to meet you"

"${greeting}," says the NPC.

Obviously, that’s a lot of work for just a single line of printed text!

In some cases, you can use the *multireplace command as a kind of “array,” but CS definitely makes you work if you want to add some spontaneity or variety to your text.

No justification

CS does not give you any control over how your text is formatted or presented except for the ability to italicize and bold words.

You cannot change the font, font size, or even justification (e.g. left-aligned, centered, etc.).

In addition, there are no header tags (e.g. “h2”) for producing text of different relative sizes.

Furthermore, unless you’re hosting a game on your own server, you cannot alter or adjust any of the CSS elements, including background color, how the buttons look, etc.

There are also no templates or “skins” that you can use to set your game-story apart in terms of how it is presented.

In essence, all CS games are required to look exactly the same. You cannot even change whether pages update by sliding to one side (called “animation” in CS-speak) or from the top down.

The only way to add customized visual elements like chapter titles in a fancy font is with the use of an embedded *image.

No timers

While still relatively rare in “conventional” forms of IF, timers are a great way to spice up the game elements of a story.

For instance, some other tools let the author set a clock of n seconds by which the player has to make their choice, after which it defaults to the author-selected default choice.

ChoiceScript definitely does not have this feature, nor does it have any option by which to delay how text is displayed on the screen, i.e. to simulate an NPC typing or data being received slowly over a slow connection, etc.

With CS, the text is always displayed onscreen immediately (unless slowed down by a lot of subroutines), and the player has an infinite amount of time to make their choices.

2 Likes

Strand Games’ authoring system Brahman allows you to turn your ChoiceScript game into an app with more options for customisation in terms of appearance.

I find the inability to centre text maddening, but it can also be done by supplying the text as an image (and adding alt-text for accessibility.)

2 Likes

That’s…actually not just ChoiceScript, that’s a limitation of how computers handle fractional numbers. They’re expressed in base 2 (binary) rather than base 10. So instead of digits for tenths, hundredths, thousandths, it has digits for halves, quarters, eighths, and so on. So you can exactly express halves, quarters, etc., but you know how in decimal one-third is 0.333… and it goes on forever, so you get weird rounding errors when you add them together? That happens to computers not only with thirds, but also with fifths or tenths or any fraction that doesn’t have a power-of-two denominator.

The usual trick, in pretty much any language, is to express things as integers (say, money in cents), and only convert to a fractional value for display. Or sometimes you can get away with rounding it to tenths after each operation? I don’t know if either of those tricks are available here: ChoiceScript is a deliberately simple tool designed to support a particular style of game.

7 Likes

Then you need to factor in “how fast can people read? How fast can they type?”

That’s not even considering whether or not the puzzle is of “analysis paralysis” type.

Oh my gosh. In choice narrative I’ve found that letting the player drop items and tracking them persistently on a map is no bueno unless it’s a particular choice system that is set up to help with this already. I believe CYS and Gruescript are two that involve inventory/location management.

I tried allowing the player to drop items in locations in Alice Aforethought and also manage two rolling looking glass mirrors in parallel worlds that were “pushable between rooms” and a portal between the locations they existed in and that was the most difficult thing to troubleshoot and test. The difficulty is that many of the prose passages didn’t represent locations, so I had to track a “location” variable instead of just marking that the player left something in their currently displayed passage.

The solution that I used in Cursed Pickle was just to never let the player drop stuff unless it was story-dictated. This went along with the MMORPG convention of the game since many online multiplayer games won’t let you litter the world with random items which would be too much for the server to keep track of. Collectibles in AoA were just random “sellable junk”, incremental quest collectibles, or weapons that could be switched out but remained in inventory.

An inventory screen can also be a major problem in a choice narrative - Ideally you want inventory available at any time, but making sure the player returns to the correct screen might cause issues, especially with a multi-page inventory where the author can’t just put a “back one page” button. This is a bug magnet if the player can access inventory from a critical screen that changes variables or world state and they return to it multiple times - the player can easily cheat on a screen that’s “Here’s a copper coin for your trouble, Adventurer” that increments a money variable if the player goes to inventory and back on that screen several times.

AXMA had a “pop over window” that made this doable - the screen that popped over the current passage could be dismissed without altering the actual page the player is on, and that could be used for a non-destructive inventory. I wish this were easier in other choice systems - I know Sugarcube has a plugin option for pop-over windows.

So without creating a lot of work for the author, often the best solution is “don’t allow dropping anything” - invent reasons that the player can’t drop items, or create very specific points in the story where the player can manage that inventory. In RSPM I restricted clothing changes in the player’s apartment (assuming that the character leaves the removed clothing in the wardrobe), and options to play with inventory only appears in locations where it’s significant and removes the menu options where it is not.

3 Likes

I certainly agree 1000% that tracking picked up and dropped items is a major chore, certainly so for ChoiceScript.

In fact, I had to put one project on ice for a bit when the item tracking system that I was coding grew to monstrous lengths :brain: :boom: :exclamation:

Fun times, fun times…