top expert

a biting cat website

Let’s Make IF s2e5: basics of properties and world state

How can we change the game world during action processing?

Last time, on let’s make IF.

In last week’s installment, we took our first look at the phases of action processing. For beginning projects, it is best to think of turns in terms of actions. As we learn more about Inform, we can think about some of the background simulations that Inform performs, but let’s focus on actions for now. As a reminder, these are the phases of action processing that we can use to construct the fabric of our game:

  • before: the earliest entry point in action processing. happens before many preconditions are checked.
  • instead: as the name suggests, “instead” rules are good for redirecting or stopping requested actions.
  • check: typically, an evaluation before processing.
  • carry out: the action itself.
  • after: feedback after the action is complete, typically prevents “report” rules from firing.
  • report: a final message or phrase when an action is concluded.
  • every turn: just as it says, a rule that is evaluated at the end of every turn. not necessarily specific to the player’s command.

Last time, my focus was on text output. But what else can be done?

a simple example of world state.

There are many things that are tracked and updated while simulating a game world. As this is merely an introduction, let’s start by thinking about properties. Not so long ago, we talked about clothing and how it can be characterized as “worn by” or “not worn by” a player (or any other person in the game, but let’s stick with the player). Those two possibilities are part of the Standard Rules. Since we talked about the “frobbing” action last time, let’s continue that line of thought. What if we wanted to track whether something or not had been “frobbed?”

a thing can be frobbed.

we ought to set a default for the things we make in-game. This is a sweeping rule that will apply to every in-world thing. Unless stated otherwise, things are not frobbed.

a thing is usually not frobbed.

“Usually” is important here! “Always” would make everything immutably not frobbed. It is likewise not possible to change something if our code says “a thing is frobbed.” “Usually” will get us what we want: a default that can change.

As a reminder, we can use properties like “frobbed” to change output.

the description of the apple is "It certainly looks tasty[if the apple is frobbed], but you'd rather not eat things that have been frobbed[end if]."

Side note: notice where punctuation is placed, as this is important when creating variable texts.

The question is, “how does something become ‘frobbed?’”

getting frobbed.

As you might guess, we can add, remove, or check the “frobbed” property of something during action processing. Let’s think about what we might want to do. Here’s a short list of things that might be applicable:

  1. prevent something from being frobbed more than once.
  2. change something from not frobbed to frobbed when the player uses the frobbing action.
  3. come up with generic and specific frob responses.

Let’s start with number one. Usually, these preventative measures happen at the beginning of action processing: before, instead, and check. While some things are a matter of taste, I usually use “instead” rules if the intent is to stop an action altogether. It’s also good to leave “before” in reserve, in case you every need to preempt an “instead.”

Let’s do something like this:

frobbing is an action applying to one thing.
understand "frob [something]" as frobbing.
instead of frobbing a frobbed thing:
	say "You can't frob something that has already been frobbed."

Inform is quite flexible regarding some of its phrasing. We could likewise say

instead of frobbing something frobbed:

or even

instead of frobbing something when the noun is frobbed:

What is recommended? As in many cases, my answer is that you should determine which is easier for you to read! The more readable your code is, the easier it will be to troubleshoot. My only advice is to choose a method that makes sense to you and stick with it.

This will stop some commands, but nothing yet exists to change whether something is frobbed or not. I like to handle things like that during the “carry out” phase.

carry out frobbing something:
	now the noun is frobbed.

This is all it takes! “now” is required when we change something in the world. It’s significant for multiple reasons. For one, the “frobbed” property is being added “now,” IE, during the “carry out” phase of the turn. If we have Inform check later (“after,” “report,” or “every turn”), the “frobbed” property will be applied.

“Noun” as used in code is what is called a token. It can be substituted for the noun in a player’s command. For instance, if the player types “frob apple,” we can use a general rule that includes the apple with “now the noun is frobbed.”

This “carry out” rule assumes that we have already dealt with things we don’t want the player to frob. Anything that makes it this far is going to be frobbed. Our “instead” rule above does this for a specific case. Any other unwanted actions would have to be dealt with in a similar way.

feedback.

“Report” rules are good for generic feedback. By the time a “report” rule is relevant, our “carry out” rule has assigned the “frobbed” property to any applicable noun. That rule doesn’t tell the player anything about their action, though: there’s no feedback. Here’s a generic feedback rule.

report frobbing something:
	say "Frobbed.";

As in our “carry out” rule, the assumption is that only successes have gotten this far. This is not a very interesting bit of feedback, though. It probably is just good for giving feedback for generic things that aren’t important.

We can tailor specific rules for things that might matter more. With an “after” rule, we can supersede the report.

after frobbing the apple:
	say "Oh my goodness! I can't believe you frobbed the apple! That doesn't look very appetizing.";

“After” rules stop action processing, so our generic “report” rule will never do anything if we have a custom “after” rule. If we have a specific need to continue, we could add

	continue the action.

to our “after” rule, but in this case printing the generic report rule will just seem weird. Still, there will be cases where this matters. Like some other instances, it is best to avoid saying things like “continue the action” unless you have something very specific in mind.

about “every turn” rules.

Even though “every turn” is a phase of action processing, it occurs independently from the other phases. “Instead” rules do not circumvent the “every turn” phase, for instance. “Every turn” rules are usually used for managing and responding to the game world itself rather than handling specific player actions. These are often what I call “big rules.”

postscript: naming “big” rules.

I usually refer to rules (Inform code is made up of “rules”) that cover many things or that generally apply to the whole game world as “big” rules. I prefer giving such big rules names of their own. We won’t be looking at debugging and troubleshooting for a while, but this is a good habit to get into. How are rules named? Let’s look at some of the big rules in this post:

instead of frobbing a frobbed thing (this is the can't frob what's frobbed rule):

and

carry out frobbing something (this is the general frobbing rule):

and

report frobbing something (this is the default robbing response rule):

The first line of a rule is called a “preamble.” Naming a rule is simple. We just declare it in parenthesis. The syntax is important: “(this is the … rule)”.

In my projects, I don’t find it necessary to name many rules, but large ones that appear a lot (or complex ones than might tend to break) usually get their own names.

All code from this post can be reviewed in this Borogove snippet.

next.

What about numbers? A basic introduction to numbers and world state.

Categories: , ,

Leave a comment