Observations on the IF Game Project

📅 2023-03-07

🏷 Gaming

I have now set up a functional Gemini interface for this, although there is nothing publicly available yet. I have plenty of fine-tuning to do in the user interface to see what works best in this use case.

I've collected some observations on the project so far. All in all, it feels very much like working on a point-and-click adventure game, but instead of graphics you have lots of text and get to use your imagination.

Fit for Gemini

In general, the Gemini interface feels pretty nice. On a fundamental level, text adventures are a perfect fit for Gemini: you just have a bunch of text and hyperlinks for interactivity. I believe that people who are drawn to Gemini due to its text-orientation are also likely to enjoy text adventure games (and maybe even good old-fashioned books).

One detail in the interface I'm still unsure about is how important it is to keep the past actions of the player visible. Currently, each page just shows the current location's or action's description, and related action links. The player is expected to remember what they did during the few last turns. One possibility is to add a "Journal" page with a list of the past actions, or just a "mini journal" with a few entries in the bottom of the page. These should provide enough context even when resuming the game after a longer break.

The GmCapsule integration went according to expectations. It was not quite as trivial as the terminal input prompt because it needed to be adapted to a multithreaded environment. For example, I had to get rid of some global state, and ensure that each game is processed by only one worker thread at a time. The server keeps ongoing game sessions in memory but they are also saved to disk after each turn. Any server worker thread can pick up one of the ongoing games. There will be a need to purge expired game sessions, after which they will be reloaded from the saved state if the client comes back.

Serialization of game state is pretty straightforward in this kind of a game. Only the dynamic part of the world needs to be saved, for example objects' parent relationships, deletion status, and variable values. The rest can be used as-is from the initial parsing of the configuration files. This makes it possible to quickly save and load the state at any time.

One storyline vs. branching into multiple storylines

This is one of the fundamental game design considerations: how much do the player's actions impact the progression of the story and world events. Pragmatically, it is not feasible to allow the player to dramatically alter the storyline, unless one is creating a simulator. Story-based games usually allow influencing a few details at best, like choosing which one of your companions survives in Mass Effect, or having multiple alternative endings depending on earlier choices. The more open-ended things get, the more challenging it is to put together a meaningful story.

I haven't played real-life role-playing games in many years, but working on this project reminds me of a few attempts to write and DM a game for a group of friends. It seems I prefer a strong linear-ish storyline.

Can the player die?

I'm leaning on "no". This brings back memories of LucasArts vs. Sierra adventure games. Allowing the player to die changes the nature of the game, making it quite a bit harder and introduces frustrating trial and error. While there may be a moment or two where death could be permitted to increase tension, I don't find it that fun. Better to allow only actions that don't lead to death. Ill-advised or misfortunate actions should lead to a setback at most.

Task tracking and other accommodations

It helps a lot if the player is aware of what needs to done at any given time. It's very annoying when you're stuck in a game not quite sure what to do next, so you end up wandering around and/or brute forcing random things, or just abandon the game entirely. On the other hand, too much handholding makes things too easy or boring. It's important to define tasks given to the player from the point of view appropriate to the player character's current knowledge, and not based on the game designer's omniscience.

I've found that it's super distracting if the discovery and/or completion of tasks is announced in the regular text output. It seems to break the immersion. I'll likely just add a badge in the "Tasks" link when there are changes to the task tree, and the player can check it out when it's not a distraction.

Another convenience is fast travel via the map. It seems tedious to force the player to traverse a set of known locations and re-read the unchanged description of each. I had fun implementing a little A* path finding algorithm to traverse the connections between locations to find a route to where the player wants to go. Care will need to be taken so the path is interrupted if something unexpected occurs, like if a passage is blocked for some reason.

Complexity of the engine

Previously I was lamenting the complexity of a full-blown text adventure engine like TADS, but even this tiny engine is trending toward some degree of complexity — there is a certain level of functionality required to create an interactive experience.

After finishing the core of the engine, special situations still come up that require small additions. For example, I needed a directive for choosing a random outcome. It seems the configuration files are turning into a simple scripting language. It's not that surprising given the core elements: objects, variables, expressions with operators, simple if/else control flows. Loops are still missing but I haven't found a need for them yet. There is also a distinction between parse-time and execution-time behavior: parsing the configuration yields the initial state of the world, and each object then has a bunch of little scripts that get executed based on user actions. There are a few specialized keywords for declaring "fixtures" (non-takeable items like scenery) and "passages" (connections between locations).

When it comes to writing the configuration files in practice, it has been a great help to make a TextMate language grammar that enables Visual Studio (and TextMate) to apply syntax highlighting in the files. VS Code also has automatic folding based on indentation, which helps greatly in hiding parts of the file not currently relevant. Quite similar to full-fledged programming, really, only done in a more declarative fashion.

Constructing the world

I'm pretty happy with the three-chapter sci-fi story outline that I've managed to put together. It's nothing immensively novel, I would say, taking a bunch of influences from popular movies and books. Instead of complete originality, my main concern is to keep the story and world internally coherent and plausible in the context of sci-fi. I don't have a lot of practice with this kind of writing so it's better to keep things grounded.

I've designed an environment where the player can roam around. It now needs to be filled with background elements and items needed for the story and puzzles.

It has been a fun challenge taking the story outline and constructing concrete scene descriptions based on it. This entails straightforward details about the scenery, coming up with a series of engaging objectives, managing intentional ambiguities ("mystery"), and misdirecting the player while keeping things coherent for the story ("twists!"). As much as possible, I'm using the same object structure for all game elements, e.g., containers, player inventory, items one can interact with, and dialogue trees. You can do quite a lot with conditional actions based on state variables.

Puzzle design in general is a big topic of its own, and much depends on the engine's capabilities. Something I still haven't tackled (but definitely want to) are mini puzzles like the "hacking minigames" in many sci-fi games. I would call these a staple of the genre. It will be fun to make a text version of this kind of mini puzzle game.

Going forward, a big concern is the same as with all of my ongoing projects: finding time and energy among family/work duties and the many interruptions of daily life. The larger and more intricate the world grows, the more difficult it is to keep it in one's head. I'm hoping to limit the scope of the story accordingly.


Finally, I need a plan for publishing the game. Putting out something half-finished is unappealing. A reasonable option would be to split the story from the main engine repository and first publish the engine itself with a tiny demo game in a completely different setting. This could be used to polish the user interface before the main game is ready.

A serialized release is also possible: the chapters would be released separately when they're finished. This does require planning all of them in quite a lot more detail, though, as sometimes elements from later chapters need to be inserted earlier into the storyline.

CC-BY-SA 4.0

The original Gemtext version of this page can be accessed with a Gemini client: gemini://skyjake.fi/gemlog/2023-03_if-observations.gmi