where were we?
Those of you have been following this content since the tumblr days probably remember some posts about rooms collapsing based on timers. This isn’t hard to do; as always, it’s the details that challenge us. It’s easy to tell inform that a thing, or kind of thing, has a value associated with it. In my old examples, it was as simple as this:
a room has a number called countdown.
We can set a default value for every room:
a countdown is usually three.
The countdown can decrement every turn, and when it reaches zero, that can be used to trigger something. In those earlier discussions, I stressed the importance of considering when the countdown number changes. Because Inform has an “every turn” rulebook built in, that is an easy enough place to start.
every turn when the countdown of the location is not zero:
decrement the countdown of the location.
We’ll have to add something else for when the countdown is zero. We want to make sure both down fire in the same turn (that is, decrement to zero and immediately do whatever is meant to execute on turn zero). I’d marry the two in a single rule:
every turn:
if the countdown of the location is not zero:
decrement the countdown of the location;
otherwise:
end the story saying "game over".
This will only let one or the other fire. I don’t like it very much, though, because I’d like to qualify “every turn” in some way. Such nonspecific rules can lead to unexpected behavior.
Note that kind of vagueness will almost certainly not lead to a performance problem. My concern is that I want to avoid unhelpful debug information from RULES ON output. Over the course of a game, that output can become quite busy! Fortunately, we’ll have a chance to whittle things back in a moment.
sidebar: i don’t want to have rooms collapse, why should i care about this
Now and again, a critic will assert that “interactive fiction” (in the narrow sense of a product sold by Infocom in the 1980s) is “not really fiction.” I find such claims a little reductive–fiction is not necessarily a cascade of events–but I do grant that parser games often lack urgency or momentum. Time in those classic games is usually just a kind of expendable resource. In Zork I and II, the batteries in the lamp last a set number of turns. While that sort of design does require player efficiency, it hardly ever feels urgent from turn to turn.
On the other hand, in other games, the passage of time affects the game world visibly. There is the earthquake in Zork III, in which one passage closes and another opens. In both cases, the change is irreversible. This potentially game-breaking event has upset players over the years, but changing the world rather than an object (the lamp’s batteries, in our examples) was a momentous innovation. In Enchanter, the world deteriorates with each passing day. The nights grow longer, and enemies are emboldened.
These are effective ways to complicate the player’s apprehension of the game world. The lamp could be handled in a very similar way.
every turn when the lamp is lit:
decrement the battery life.
We could make things more dramatic by printing ominous warnings when the battery life reaches certain thresholds.
All the same, this design, while encouraging efficiency, will not necessarily create suspense moment-to-moment. Perhaps a protagonist has only a moment or two to tell a love interest their true feelings, before that person is gone for their life forever. That might be done with a timer. A fuse could be lit. A tank might fill with water. Any event that is brief and localized might be handled with a simple countdown.
just when is when: thoughts on action processing and timers
A lot happens in action processing. In fact, the whole game happens during action processing! Because an Inform 7 world can be complex with many moving parts, games process rules sequentially (any possible exceptions are beyond the comprehension of we mere mortals!). Even rules that appear to have no particular priority (instead rules, for instance, are all in one rulebook) will often be processed according to their place, top to bottom, in the source code. If you have a decent-sized game (60k words, perhaps), you will not want to figure out where rules are situated within your source code.
For this and many other reasons, you will spare yourself a generous helping of heartache if you write rules that are a) specific and b) thoughtfully situated within action processing.
In a simple countdown rule, consider when things should happen. Every turn rules happen at the very end of action processing. That means that the player will do whatever they were trying to do, then the number will change. However, you might the “zero event” (room collapse, romantic interest leaving for good, whatever) to happen BEFORE the player acts. I’ll break up our earlier rule:
every turn when the countdown of the location is not zero:
decrement the countdown of the location.
before doing something when the countdown of the location is zero:
end the story saying "game over".
Now, in reality, we probably wouldn’t want to end the player’s game for good. We’d probably want to reset the count and warn the player. Or some such thing. Or maybe the zero count would trigger a truth state that we could use to end the countdown for good.
But wait… last week, we had some “first before” rules. How will that effect things?
Quite dramatically, in fact. As a memory jogger, here is one of those rules:
first before xyzzying something with something magical when the player is tinert:
if the second noun is in the location:
try xyzzying the noun instead;
otherwise:
say the parser error internal rule response (E) instead.
Since we have “instead” at the end, if Inform processes this rule first, that will be the end of it; our other rule will never fire! Since we don’t want to get in a situation where we are troubleshooting our code spatially (locating its place in a sequence), we’ll need to reconsider our ordering. I think we’ll keep the timer rule as a before and move these other rules to “instead.” Looking at everything together:
instead of xyzzying something with something that is not magical when the player is tinert:
say "The [second noun] is not a viable source of magic.".
instead of xyzzying something with something that is not magical when the player is not tinert:
try assaying the player.
[and so forth]
before doing something when the countdown of the location is zero:
end the story saying "game over".
Should we keep the “first” designations? It depends. If your systems are elaborate, you may find yourself needing to guarantee that specific rules fire first. Because of the scope changes we implemented last time, this may be especially important.
Ideally, though, we aren’t just doing a ton of “instead rules.” Overreliance on instead is a common beginner’s pitfall. It’s easy to see why. Instead rules usually work, but when things become complex, or when actions stop working, it can be hard to figure out which of fifty instead rules are shutting things down.
Drew’s thoughts on action processing (listed in the order that they are processed):
- After reading a command: dark sorcery, pre-empting turn processing altogether. Use with caution!
- Before: good for general rules, as before can be applied without specificity (“before doing something”). By default, permits action processing to continue.
- Instead: used to stop or redirect actions that have made it through the before stage. Can also be used generally.
- Check: used as a final evaluation before executing an action. Note that “check” is specific to an action. You can’t use phrases like “check doing something.” At this point, though, it is really too late for that sort of “catch all” processing.
- Carry out: this is it! The player’s action is actually happening. This is also specific. Make changes to world state, give feedback, etc.
- After: for a long time, I thought “after” was the final stage of action processing. It isn’t! That’s report (see below). Like before and instead, after rules can be applied generally. I like to use them for a scoring system (more soon!) based on this example from the documentation.
- Report: another specific rule. It’s intended for feedback on completed actions, but I usually just do that in the carry out phase. If I need to append more code to an action for a narrow, specific situation, I might do it here.
- Every turn: rules for the general machinery of the game world. It is best to use very specific constructions to avoid confusion.
People have different preferences, but I strongly encourage you to consider how you can use the entire turn in your code. It will help you troubleshoot problems and, ultimately, it will give you more flexibility while coding.
next
That’s it for today, but I don’t think we’re done yet. We’ll take a last look at time and urgency next time.

Leave a comment