§27.30. To say one of

Many of the invocation syntaxes described in the previous section are used in the definition by the Standard Rules of the "[one of] ... [or] ... [purely at random]" construction, so it makes a good example of how they can be used.

First, this is a segmented substitution with a single possible beginning ("[one of]"), a single possible middle ("[or]") but a choice of many possible endings. Almost everything is compiled by the invocation of the beginning:

To say one of -- beginning say_one_of:
    (- {-allocate-storage:say_one_of}I7_ST_say_one_of-->{-counter:say_one_of}
    = {-final-segment-marker}(I7_ST_say_one_of-->{-counter:say_one_of}, {-segment-count});
switch((I7_ST_say_one_of-->{-advance-counter:say_one_of})%({-segment-count}+1) - 1) {
    0: -).
To say or -- continuing say_one_of:
    (- {-segment-count}: -).
To say purely at random -- ending say_one_of with marker I7_SOO_PAR:
    (- {-close-brace} -).

The 3rd invocation of this (say) might compile the following:

I7_ST_say_one_of-->2 = I7_SOO_PAR(I7_ST_say_one_of-->2, 4);
switch((I7_ST_say_one_of-->2)%5 - 1) {
    0: ... first text ...
    1: ... second text ...
    2: ... third text ...
    3: ... fourth text ...
}

First, we notified Inform that it needs to allocate an array (I7_ST_say_one_of) providing storage associated with the counter "say_one_of". This we used to count off individual invocations of "[one of]", so that each would have its own word of storage - for the 3rd invocation, I7_ST_say_one_of-->2. We then call a state-changing routine, in this case I7_SOO_PAR, which is allowed to know the previous state and also the number of options available, and which returns the new state. The state is supposed to be the option chosen last time, but that means that there are not 4, but 5 possibilities: 0 for "there was no last time", then 1 to 4 for the possible outcomes. We reduce the state mod 5 to obtain the decision this time, and subtract 1 because it happens to be convenient to make the switch statement run from 0 to 3 rather than 1 to 4. (The reason we reduce the state mod 5 is to allow the state-changer to squirrel away secret information in the upper bits of the state, if it wants to. Note that subtracting one means that the switch value might be -1, which results in no text being printed: thus if the state-changer chooses 0, it can decide on none of the above.)

In this design, the marker attached to the choice of ending substitution is the name of the I6 state-changer: here is the I7_SOO_PAR routine.

[ I7_SOO_PAR oldval count; if (count <= 1) return count; return random(count); ];

As it happens, this ignores the old value: after all, it is meant to be purely at random, and nothing could be less pure than taking the last outcome into consideration when choosing the next.

Note that the counter say_one_of is advanced in invocation of the head. It might seem that the tidier design, somehow, would be to advance the counter in the invocation of the tails, but this is not a good idea. In general it is not safe to assume that the counter will have the same value when the tail is invoked that it had when the head was invoked, because segmented say constructions can legally be nested in Inform strings. Because of this, it is best to deal with a counter entirely in a single invocation, either of the beginning or the ending.

Because "[one of] ... [or] ..." is such a useful construction - switching between alternative forms of text, which writers of IF very often do - the above implementation is intentionally left open for new endings to be added, and the examples below show how easily this can be done.


arrow-up.pngStart of Chapter 27: Extensions
arrow-left.pngBack to §27.29. Invocation labels, counters and storage

Suppose we are writing a game in which the mood of the piece changes, and we would like to have lots of descriptions that vary according to its current state. We might in that case want to create our own "by atmosphere" token, to control text variations, like this:

paste.png "Blink"

Atmosphere is a kind of value. The atmospheres are normal, melancholy, and creepy.

The current atmosphere is an atmosphere that varies.

To say by atmosphere -- ending say_one_of with marker I7_SOO_ATM:
    (- {-close-brace} -).

Since we're operating within the untyped Inform 6, we can make use of the fact that kinds of value are (internally) just constants, enumerated in the same order in which they were originally defined. In other words, "normal" at the I6 level translates to 1, "melancholy" to 2, and "creepy" to 3; so we can return the value of the current atmosphere, and thereby select option 1, 2, or 3:

Include (-
[ I7_SOO_ATM oldval count;
    if (count < (+ current atmosphere +)) return count;
    return (+ current atmosphere +); ];
-)

And that concludes the hard part. Now to test that it works:

The Flat is a room. "A small [one of]but cozy[or]depressing[or]imprisoning[by atmosphere] flat. Outside the window, the sun is [one of][or][or]apparently [by atmosphere]shining and there is a brisk breeze through the leaves of the birch trees. [one of]It would be quite nice weather for a walk[or]The rest of the world apparently has no appreciation of what you suffer[or]It all looks deceptively normal[by atmosphere]."

Instead of waiting when the current atmosphere is normal:
    say "Everything stretches wide and flat for just a moment, as though all the world around you were painted on a thin rubber sheet that is being [italic type]stretched[roman type]. Then it snaps back into place, leaving your ears ringing. But that little glitch was enough to warn you. Someone is tampering with space-time again. Someone very close by.";
    now the current atmosphere is creepy.

Test me with "look / z / look".

*ExampleBlink
Making a "by atmosphere" token, allowing us to design our own text variations such as "[one of]normal[or]gloomy[or]scary[by atmosphere]".

Suppose we are writing a game in which the mood of the piece changes, and we would like to have lots of descriptions that vary according to its current state. We might in that case want to create our own "by atmosphere" token, to control text variations, like this:

paste.png "Blink"

Atmosphere is a kind of value. The atmospheres are normal, melancholy, and creepy.

The current atmosphere is an atmosphere that varies.

To say by atmosphere -- ending say_one_of with marker I7_SOO_ATM:
    (- {-close-brace} -).

Since we're operating within the untyped Inform 6, we can make use of the fact that kinds of value are (internally) just constants, enumerated in the same order in which they were originally defined. In other words, "normal" at the I6 level translates to 1, "melancholy" to 2, and "creepy" to 3; so we can return the value of the current atmosphere, and thereby select option 1, 2, or 3:

Include (-
[ I7_SOO_ATM oldval count;
    if (count < (+ current atmosphere +)) return count;
    return (+ current atmosphere +); ];
-)

And that concludes the hard part. Now to test that it works:

The Flat is a room. "A small [one of]but cozy[or]depressing[or]imprisoning[by atmosphere] flat. Outside the window, the sun is [one of][or][or]apparently [by atmosphere]shining and there is a brisk breeze through the leaves of the birch trees. [one of]It would be quite nice weather for a walk[or]The rest of the world apparently has no appreciation of what you suffer[or]It all looks deceptively normal[by atmosphere]."

Instead of waiting when the current atmosphere is normal:
    say "Everything stretches wide and flat for just a moment, as though all the world around you were painted on a thin rubber sheet that is being [italic type]stretched[roman type]. Then it snaps back into place, leaving your ears ringing. But that little glitch was enough to warn you. Someone is tampering with space-time again. Someone very close by.";
    now the current atmosphere is creepy.

Test me with "look / z / look".

Suppose we are writing a game in which the mood of the piece changes, and we would like to have lots of descriptions that vary according to its current state. We might in that case want to create our own "by atmosphere" token, to control text variations, like this:

paste.png "Blink"

Atmosphere is a kind of value. The atmospheres are normal, melancholy, and creepy.

The current atmosphere is an atmosphere that varies.

To say by atmosphere -- ending say_one_of with marker I7_SOO_ATM:
    (- {-close-brace} -).

Since we're operating within the untyped Inform 6, we can make use of the fact that kinds of value are (internally) just constants, enumerated in the same order in which they were originally defined. In other words, "normal" at the I6 level translates to 1, "melancholy" to 2, and "creepy" to 3; so we can return the value of the current atmosphere, and thereby select option 1, 2, or 3:

Include (-
[ I7_SOO_ATM oldval count;
    if (count < (+ current atmosphere +)) return count;
    return (+ current atmosphere +); ];
-)

And that concludes the hard part. Now to test that it works:

The Flat is a room. "A small [one of]but cozy[or]depressing[or]imprisoning[by atmosphere] flat. Outside the window, the sun is [one of][or][or]apparently [by atmosphere]shining and there is a brisk breeze through the leaves of the birch trees. [one of]It would be quite nice weather for a walk[or]The rest of the world apparently has no appreciation of what you suffer[or]It all looks deceptively normal[by atmosphere]."

Instead of waiting when the current atmosphere is normal:
    say "Everything stretches wide and flat for just a moment, as though all the world around you were painted on a thin rubber sheet that is being [italic type]stretched[roman type]. Then it snaps back into place, leaving your ears ringing. But that little glitch was enough to warn you. Someone is tampering with space-time again. Someone very close by.";
    now the current atmosphere is creepy.

Test me with "look / z / look".

**ExampleUncommon Ground
Making a "by viewpoint" token, allowing us to design our own text variations such as "[show to yourself]quaint[to Lolita]thrilling[to everyone else]squalid[end show]" depending on the identity of the player at the moment.