💾 Archived View for gemini.ctrl-c.club › ~nttp › writing › intfic › stdlib.html captured on 2023-03-20 at 20:02:53.

View Raw

More Information

⬅️ Previous capture (2021-12-03)

-=-=-=-=-=-=-

<!DOCTYPE html>
<meta charset="utf-8">
<title>Designing a standard library for interactive fiction</title>
<link rel="stylesheet" type="text/css" href="diary.css">
<link rel="icon" type="image/png" href="icon.png">
<meta name="viewport" content="width=device-width">
<meta name="description" content="">
<meta name="author" content="Felix Pleşoianu">
<meta property="og:site_name" content="Fictionally interactive">
<meta property="og:type" content="website">

<header role="banner">
 <a href="https://felix.plesoianu.ro/intfic/">Fictionally interactive</a>
 <h1>Designing a standard library for interactive fiction</h1>
</header>

<time>2016-12-06</time>

<p>We usually think of programming languages and their standard libraries
as forming one coherent whole, and for general-purpose languages this
might as well be the case. But in text adventure authoring systems
the two are more divorced. Often, the language proper has few or no
features specific to interactive fiction, and it's the library that
makes it into an authoring system, properly speaking. Such is the case
with TADS 3, which has recently acquired a new standard library called
<a href='http://ericeve.livejournal.com/'>adv3lite</a> and Inform 6, which has <a href='http://ifwiki.org/index.php/Inform_6#Summary_of_Inform_Variations'>several alternatives</a>,
none especially popular.

<p>A special case is Alan 3, which I <a href="alan3-review.html">reviewed a while ago</a>, and which
has enough built in functionality that you can in principle do just
fine using just the raw language. However, it does have a standard
library made by Anssi Raisanen, which is impressively rich as such
things go.

<p>Then why did I choose to make my own instead?

<p>Why, because I could! Just kidding. But seriously, the task seemed
much less daunting than it would have been in any other authoring
system. In fact I only thought it feasible at all due to not having to
code a parser, as one is built into the interpreter. Designing the
library also helped my learning process a lot. But the main reason was
that the existing library is based on a philosophy I just couldn't
agree with. Namely, authors are expected to customize it for each and
every game. And that means forty thousand lines of code to understand
and maintain with each new project.

<p>Now, I can see why it was designed this way &mdash; certain peculiarities
in the language make the approach tempting. And if you're making an
epic on the scale of Worlds Apart for example, the overhead may not be
that big. Besides, a large proportion of those forty thousand lines,
possibly as much as half, are comments, and the author's understanding
of Alan helped me more than once when I was at a loss to figure out
how this or that feature is supposed to work.

<p>And yet, I decided that for a small project such as <a href='https://felix.plesoianu.ro/intfic/catch-that-cat/'>Catch That Cat</a>,
it was better to make something of matching size. It wouldn't have
been the first time, either: the game started out as the demo of
another authoring system, written in Javascript, and that was my main
source of inspiration.

<p>So, what's in a standard library for interactive fiction?

<p>First, of course, there are the meta verbs such as save and transcript
on/off. Except the language doesn't let you mark them as meta, so
they'll use up a turn anyway. Then the absolute basics, "look" and
"examine", to which I've also added "wait" and "search". (Also "use",
because many players, especially those less familiar with the genre
conventions, <em>will</em> try it). Basic door and device classes, too, which
are needed in my game, and last but not least a full set of
conversation verbs since the parser is complex enough to support them.

<p>Inventory management deserves special mention. It's tempting to make
the hero himself into a container (that's the PC in Alan lingo), but
that would require predefining him at the library level, and once you
do that there's no way to further customize him within the game, e.g.
by adding a description. So instead I made a separate object that
invisibly follows the hero. It's even possible in theory to replace it
with an inner location, so you can pick up and carry actors, but for
now I went with the simplest thing that could possibly work.

<pre><code>
	the player_inventory isa object
		name inventory
		description ""
		with container
			taking thing. -- So you can *attempt* to pick up actors.
			header "You are carrying:"
			else "You are empty-handed."
		verb examine does only list this. end verb.
		verb take does only list this. end verb.
	end the player_inventory.

	add to every location
		entered
			if current actor = hero then
				locate player_inventory here.
			end if.
	end add to.
</code></pre>

<p>Speaking of that, a thorny issue in IF library design is how generally
to define verbs. If actors aren't portable, for example, you might
decide to have the verb take only apply to objects (which is the Alan
default anyway), or even a Portable class defined for this purpose.
But often it makes sense to try picking something up even if the game
won't actually allow it. For example, your cat. And even for obviously
fixed objects, you may want to customize the failure message, or even
toggle the portable flag, e.g. if the hero suddenly develops
superpowers! So be as general as possible.

<p>On the other hand, I was tempted to add the darkness code to the
predefined Location class, but reconsidered for two reasons. First,
few or no rooms in any game will ever need to be darkened, so the
respective checks will be a useless overhead most of the time. Second,
having a distinct DarkRoom class means you can bypass it entirely and
roll your own mechanism if you want. Especially as I went with a
primitive implementation which doesn't consider portable light sources.
(Nor is such a thing defined in the library.)

<pre><code>
	every darkroom isa location
		is dark.
		description
			check this is not dark
			else "It's too dark to see much."
		verb examine
			check this is not dark
			else "It's too dark to get a good look."
		end verb.
	end every.
</code></pre>

<p>While on the subject of classes, I've run into a number of language
limitations. One, as mentioned in <a href='writing-alan3.html'>another article</a>, is an
inability to redirect actions. Which means that going through a locked
door would require the player to unlock and open the door manually
every time, and I didn't even want to think about having a known key
mechanism. So instead I made a rudimentary door class and allowed
players to simply pass the exit checks silently if they are carrying
the right key. There is also the fact that object properties in Alan
can't have a null value, so a door <em>must</em> have an "other side". But
that eliminates an entire class of potential programming bugs, so.

<p>(On a related note, you can't define a "catch-all" action for verbs.
You <em>can</em> group verbs in action declarations if their syntaxes are
compatible, but you can't say "for any other verb, do this". So no
Distant or Unimportant classes for you; the amount of boilerplate
required would be beyond absurd. That's also because of the strong
typing.)

<p>Another problem is that you might want to open and close objects that
aren't doors, so those verbs really have to be defined on the Object
class. And that caused a problem with the Device class, where I did
the same with "turn on/off". Which in turn required me to use the
"only" verb qualifier... and that meant that "after" refused to work
on an instance of the same class in the game. Certainly, using "before"
to schedule an event had the desired effect, but still.

<p>A final, minor addition was that of a predefined Limbo location with
all the usual compass directions defined on it. That serves the dual
purpose of having an "out of play" location that can be tested (and
where objects can be sent if needed) and letting Alan know that 'ne'
is a direction even if no exit in the game points that way. Otherwise,
since exit names are entirely arbitrary in the language, it wouldn't
know to tell me "you can't go that way".

<pre><code>
	the limbo isa location
		exit north, n, south, s, east, e, west, w to limbo.
		exit northeast, ne, southeast, se to limbo.
		exit southwest, sw, northwest, nw to limbo.
		exit up, u, down, d, 'in', out to limbo.
	end the limbo.
</code></pre>

<p>I ended up with three classes and 28 verbs (plus another 20 in the
game), spanning 327 lines of code. Most importantly, each section of
the library stands on its own. For example, if you want a game with
no inventory management, you should be able to excise the respective
code without touching anything else. Also, most of the verbs I did
include, apart from wait/use/search, have non-trivial implementations;
I really don't see the point of including sense verbs in games that
won't use them, for example. There's the illusion of freedom, and
then there's confusing the player with too many false options.

<pre><code>
	add to every object
		verb 'use' does "How, exactly?" end verb.
		verb search does "You find nothing of interest." end verb.
	end add to.
	syntax 'use' = 'use' (obj).
	syntax search = 'search' (obj).
		search = 'look' 'in' (obj).
		search = 'look' 'inside' (obj).
</code></pre>

<p>You can find the library <a href='https://felix.plesoianu.ro/intfic/catch-that-cat/catch-that-cat-alan.zip'>packaged with the game's source code</a>;
feel free to use it in your own projects, and I will consider more
additions as needed in actual games. Happy authoring!